์ž๋ฐ” ORM ํ‘œ์ค€ JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฑ…์„ ์ฝ๊ณ  ๋‚ด์šฉ์„ ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. ์ฑ…์—๋Š” ์ž์„ธํ•œ ์„ค๋ช…๊ณผ ์˜ˆ์ œ๊ฐ€ ๋งŽ์œผ๋‹ˆ ๊ผญ ๊ตฌ์ž…ํ•ด์„œ ์ฝ๋Š”๊ฒƒ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค~๐Ÿ‘


1. JPA ์†Œ๊ฐœ

1.1 SQL์„ ์ง์ ‘ ๋‹ค๋ฃฐ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ 

  • ์ž๋ฐ”๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋Œ€๋ถ€๋ถ„ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • JDBC API๋ฅผ ์‚ฌ์šฉํ•ด SQL์„ ์ „๋‹ฌํ•œ๋‹ค.

1.1.1 ๋ฐ˜๋ณต, ๋ฐ˜๋ณต ๊ทธ๋ฆฌ๊ณ  ๋ฐ˜๋ณต

๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— CURD ํ•˜๋ ค๋ฉด ๋„ˆ๋ฌด๋งŽ์€ SQL๊ณผ JDBC API๋ฅผ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค. ํ…Œ์ด๋ธ”์ด ์—ฌ๋Ÿฌ๊ฐœ๋ผ๋ฉด ๋ฌด์ˆ˜ํžˆ ๋งŽ์€ SQL์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

1.1.2 SQL์— ์˜์กด์ ์ธ ๊ฐœ๋ฐœ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ SQL์„ ์ง์ ‘ ๋‹ค๋ฃฐ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ 

  • ์ง„์ •ํ•œ ์˜๋ฏธ์˜ ๊ณ„์ธต ๋ถ„ํ• ์ด ์–ด๋ ต๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋‹ค.
  • SQL์— ์˜์กด์ ์ธ ๊ฐœ๋ฐœ์„ ํ”ผํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

1.1.3 JPA์™€ ๋ฌธ์ œ ํ•ด๊ฒฐ

JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•  ๋•Œ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ SQL์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

// ์ €์žฅ
jpa.persist(member);

// ์กฐํšŒ
String memberId = "helloId"
Member member = jpa.find(Member.class, memberId);

// ์ˆ˜์ •
Member member = jpa.find(Member.class, memberId);
member.setName("์ด๋ฆ„๋ณ€๊ฒฝ"); // jpa๋Š” update ๋ฉ”์†Œ๋“œ๊ฐ€ ๋”ฐ๋กœ ์—†๋‹ค.

// ์—ฐ๊ด€๋œ ๊ฐ์ฒด ์กฐํšŒ
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();

1.2 ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜

  • ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ง€ํ–ฅํ•˜๋Š” ๋ชฉ์ ์ด ์„œ๋กœ ๋‹ค๋ฅด๋‹ค.
  • ๊ธฐ๋Šฅ๊ณผ ํ‘œํ˜„ ๋ฐฉ๋ฒ•๋„ ๋‹ค๋ฅด๋‹ค.
  • ๊ฐ์ฒด ๊ตฌ์กฐ๋ฅผ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ์— ์ €์žฅํ•˜๋Š” ๋ฐ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค.

1.2.1 ์ƒ์†

๊ฐ์ฒด๋Š” ์ƒ์†์ด๋ผ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ ํ…Œ์ด๋ธ”์€ ์ƒ์†์ด๋ผ๋Š” ๊ธฐ๋Šฅ์ด ์—†๋‹ค.(์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ƒ์† ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€๋งŒ ๊ฐ์ฒด์˜ ์ƒ์†๊ณผ๋Š” ์•ฝ๊ฐ„ ๋‹ค๋ฅด๋‹ค.)

๊ฐ์ฒด์ƒ์†๋ชจ๋ธ


๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ชจ๋ธ๋ง์—์„œ๋Š” ์Šˆํผํƒ€์ž… ์„œ๋ธŒํƒ€์ž… ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์‚ฌํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

ํ…Œ์ด๋ธ”๋ชจ๋ธ


์œ„์˜ ๊ตฌ์กฐ๋ฅผ JDBC API๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์ž‘์„ฑํ•ด์•ผ ํ•  ์ฝ”๋“œ๋Ÿ‰์ด ๋งŒ๋งŒ์น˜ ์•Š๋‹ค.

  • INSERT INTO ITEMโ€ฆ
  • INSERT INTO ALBUMโ€ฆ


JPA์™€ ์ƒ์†
JPA๋Š” ์ƒ์†๊ณผ ๊ด€๋ จ๋œ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ๊ฐœ๋ฐœ์ž ๋Œ€์‹  ํ•ด๊ฒฐํ•ด ์ค€๋‹ค.

// ์ €์žฅ
jpa.persist(album);

// JPA์—์„œ ์‹คํ–‰ ํ•ด์ฃผ๋Š” SQL
INSERT INTO ITEM...
INSERT INTO ALBUM...


1.2.2 ์—ฐ๊ด€๊ด€๊ณ„

  • ๊ฐ์ฒด๋Š” ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค๋ฅธ ๊ฐ์ฒด์™€ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„๋‹ค. ์ฐธ์กฐ์— ์ ‘๊ทผํ•ด์„œ ์—ฐ๊ด€๋œ ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • ํ…Œ์ด๋ธ”์€ ์™ธ๋ž˜ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”๊ณผ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„๋‹ค. ์กฐ์ธ์„ ์‚ฌ์šฉํ•ด์„œ ์—ฐ๊ด€๋œ ํ…Œ์ด๋ธ”์„ ์กฐํšŒํ•œ๋‹ค.
  • ๊ฐ์ฒด๋Š” ์ฐธ์กฐ๊ฐ€ ์žˆ๋Š” ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
์—ฐ๊ด€๊ด€๊ณ„


๊ฐ์ฒด๋ฅผ ํ…Œ์ด๋ธ”์— ๋งž์ถ”์–ด ๋ชจ๋ธ๋ง

// ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์— ๋งž์ถ”๋ฉด ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ์กฐํšŒํ•  ์ˆ˜ ์—†๋‹ค.
class Member {
    String id;   // MEMBER_ID ์ปฌ๋Ÿผ ์‚ฌ์šฉ
    Long teamId; // TEAM_ID FK ์ปฌ๋Ÿผ ์‚ฌ์šฉ
}


๊ฐ์ฒด์ง€ํ–ฅ ๋ชจ๋ธ๋ง

// ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ์†Œ๋ชจํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋งŽ๋‹ค.
class Member {
    String id;   // MEMBER_ID ์ปฌ๋Ÿผ ์‚ฌ์šฉ
    Team team; // ์ฐธ์กฐ๋กœ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ๋Š”๋‹ค.
}

// ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ
member.getId();
member.getTeam.getId(); // TEAM_ID FK๋ฅผ ์ง์ ‘ ๊ตฌํ•ด์•ผ ํ•จ
...์ƒ๋žต...

// ์กฐํšŒ
SQL ์‹คํ–‰
Member member = new Member ();
Team team = new Team();
...์ƒ๋žต...
member.setTeam(team); // ์ง์ ‘ ํšŒ์›๊ณผ ํŒ€ ๊ด€๊ณ„ ์„ค์ •


JPA์™€ ์—ฐ๊ด€๊ด€๊ณ„

// JPA๋Š” ์—ฐ๊ด€๊ด€๊ณ„์™€ ๊ด€๋ จ๋œ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ค€๋‹ค.

// ์ €์žฅ
member.setTeam(team);
jpa.persist(member); // TEAM_ID FK๋ฅผ ์ง์ ‘ ์ง€์ •ํ•  ํ•„์š” ์—†์Œ

// ์กฐํšŒ
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam(); // ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ


1.2.3 ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰

๊ฐ์ฒด์—์„œ ํšŒ์›์— ์†Œ์†๋œ ํŒ€์„ ์กฐํšŒํ•  ๋•Œ๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์—ฐ๊ด€๋œ ํŒ€์„ ์ฐพ์œผ๋ฉด ๋˜๋Š”๋ฐ, ์ด๊ฒƒ์„ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰์ด๋ผ ํ•œ๋‹ค.

๊ฐ์ฒด๊ทธ๋ž˜ํ”„ํƒ์ƒ‰


// ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰
member.getOrder().getOrderItem()...

// ๋ฌธ์ œ ๋ฐœ์ƒ
member.getOrder(); // ๋งŒ์•ฝ null์ด๋ผ๋ฉด?

SQL์„ ์ง์ ‘ ๋‹ค๋ฃจ๋ฉด ์ฒ˜์Œ ์‹คํ–‰ํ•˜๋Š” SQL์— ๋”ฐ๋ผ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ์–ด๋””๊นŒ์ง€ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ •ํ•ด์ง„๋‹ค. ์ด๋Š” ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๊ฐ€ ์–ธ์ œ ๋Š์–ด์งˆ์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ํ•จ๋ถ€๋กœ ํƒ์ƒ‰ํ•  ์ˆ˜ ์—†๋‹ค.

JPA์™€ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰
JPA๋Š” ์—ฐ๊ด€๋œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์ ์ ˆํ•œ SELECT SQL์„ ์‹คํ–‰ํ•œ๋‹ค(์ง€์—ฐ๋กœ๋”ฉ). ๋”ฐ๋ผ์„œ JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฐ๊ด€๋œ ๊ฐ์ฒด๋ฅผ ์‹ ๋ขฐํ•˜๊ณ  ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

1.2.4 ๋น„๊ต

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๊ธฐ๋ณธ ํ‚ค์˜ ๊ฐ’์œผ๋กœ ๊ฐ row๋ฅผ ๊ตฌ๋ถ„
  • ๊ฐ์ฒด๋Š” ๋™์ผ์„ฑ, ๋™๋“ฑ์„ฑ ๋น„๊ต.
    • ๋™์ผ์„ฑ์€ == ๋น„๊ต. ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค์˜ ์ฃผ์†Œ ๊ฐ’์„ ๋น„๊ตํ•œ๋‹ค.
    • ๋™๋“ฑ์„ฑ์€ equals() ๋น„๊ต. ๊ฐ์ฒด ๋‚ด๋ถ€์˜ ๊ฐ’์„ ๋น„๊ตํ•œ๋‹ค.

JDBC API ๋น„๊ต

String memberId = "100";
Member member1 = memberDAO.getMember(memberID);
Member member2 = memberDAO.getMember(memberID);

// false
member1 == member2;


JPA ๋น„๊ต

String memberId = "100";
Member member1 = jpa.find(memberID);
Member member2 = jpa.find(memberID);

// true
member1 == member2;


1.2.5 ์ •๋ฆฌ

  • ๊ฐ์ฒด ๋ชจ๋ธ๊ณผ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ชจ๋ธ์€ ์ง€ํ–ฅํ•˜๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์ด ์„œ๋กœ ๋‹ค๋ฅด๋‹ค.
  • ์ด ํŒจ๋Ÿฌ๋‹ค์ž„ ์ฐจ์ด๋ฅผ ๊ทน๋ณตํ•˜๋ ค๊ณ  ๊ฐœ๋ฐœ์ž๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ์‹œ๊ฐ„๊ณผ ์ฝ”๋“œ๋ฅผ ์†Œ๋น„ํ•œ๋‹ค.
  • ๊ฒฐ๊ตญ, ๊ฐ์ฒด ๋ชจ๋ธ๋ง์€ ํž˜์„ ์žƒ๊ณ  ์ ์  ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ์˜ ๋ชจ๋ธ๋กœ ๋ณ€ํ•ด๊ฐ„๋‹ค.
  • JPA๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , ์ •๊ตํ•œ ๊ฐ์ฒด ๋ชจ๋ธ๋ง์„ ์œ ์ง€ํ•˜๊ฒŒ ๋„์™€์ค€๋‹ค.



1.3 JPA๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

JPA๋ž€?

  • JPA(java persistence API)๋Š” ์ž๋ฐ”์ง„์˜ ORM ๊ธฐ์ˆ  ํ‘œ์ค€์ด๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ JDBC ์‚ฌ์ด์—์„œ ๋™์ž‘ํ•œ๋‹ค.
JPA



ORM์ด๋ž€?

  • ORM(Object-Relational Mapping)์€ ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋งคํ•‘ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.
  • ๋‹ค์–‘ํ•œ ORM ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์ด ์žˆ๋Š”๋ฐ ์ž๋ฐ” ์ง„์˜์—์„œ๋Š” ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.
JPA์กฐํšŒ




1.3.1 JPA ์†Œ๊ฐœ

JPAํ‘œ์ค€์ธํ„ฐํŽ˜์ด์Šค์™€๊ตฌํ˜„์ฒด


  • JPA๋Š” ์ž๋ฐ” ORM ๊ธฐ์ˆ ์— ๋Œ€ํ•œ API ํ‘œ์ค€ ๋ช…์„ธ๋‹ค.
  • ์‰ฝ๊ฒŒ ์ด์•ผ๊ธฐํ•ด์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ชจ์•„๋‘” ๊ฒƒ์ด๋‹ค.
  • ๋”ฐ๋ผ์„œ JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด JPA๋ฅผ ๊ตฌํ˜„ํ•œ ORM ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์„ ํƒํ•ด์•ผ ํ•œ๋‹ค.
  • ORM ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ๊ฐ€์žฅ ๋Œ€์ค‘์ ์ด๋‹ค.


1.3.2 ์™œ JPA๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๊ฐ€?

  • ์ƒ์‚ฐ์„ฑ
  • ์œ ์ง€๋ณด์ˆ˜
  • ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜ ํ•ด๊ฒฐ
  • ์„ฑ๋Šฅ (์บ์‹œ)
  • ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ์ถ”์ƒํ™”์™€ ๋ฒค๋” ๋…๋ฆฝ์„ฑ
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งˆ๋‹ค ์‚ฌ์šฉ๋ฒ•์ด ๋‹ค๋ฅด๋‹ค (์˜ˆ: ํŽ˜์ด์ง• ์ฟผ๋ฆฌ)
    • JPA์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ ์•Œ๋ ค์ฃผ๋ฉด ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ๋œ๋‹ค
  • ํ‘œ์ค€
    • ์ž๋ฐ” ์ง„์˜ ORM ๊ธฐ์ˆ  ํ‘œ์ค€
    • ํ‘œ์ค€์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ๊ตฌํ˜„ ๊ธฐ์ˆ ๋กœ ์†์‰ฝ๊ฒŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
๋ฒค๋”๋…๋ฆฝ์„ฑ



1.4 ์ •๋ฆฌ

  • SQL์„ ์ง์ ‘ ๋‹ค๋ฃฐ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฌธ์ œ
  • ๊ฐ์ฒด์ง€ํ–ฅ ์–ธ์–ด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์ด์˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ
  • JPA๊ฐ€ ๊ฐ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜๋Š”์ง€
  • JPA๊ฐ€ ๋ฌด์—‡์ธ์ง€
  • JPA ์žฅ์ 



2. JPA ์‹œ์ž‘

2.1 ์ดํด๋ฆฝ์Šค ์„ค์น˜์™€ ํ”„๋กœ์ ํŠธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

2.2 H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์น˜

2.3 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

2.4 ๊ฐ์ฒด ๋งคํ•‘ ์‹œ์ž‘

// ๊ฐ์ฒด ๋งคํ•‘ ์˜ˆ
@Entity
@Table(name="MEMBER")
public class Member {

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "NAME")
    private String username;

    private Integer age;
    ...
}
์–ด๋…ธํ…Œ์ด์…˜ ์„ค๋ช…
@Entity ์ด ํด๋ž˜์Šค๋ฅผ ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘ํ•œ๋‹ค๊ณ  JPA์—๊ฒŒ ์•Œ๋ ค์ค€๋‹ค. @Entity๊ฐ€ ์‚ฌ์šฉ๋œ ํด๋ž˜์Šค๋ฅผ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ผ ํ•œ๋‹ค.
@Table ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์— ๋งคํ•‘ํ•  ํ…Œ์ด๋ธ” ์ •๋ณด๋ฅผ ์•Œ๋ ค์ค€๋‹ค. ์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์ƒ๋žตํ•˜๋ฉด ํด๋ž˜์Šค ์ด๋ฆ„์„ ํ…Œ์ด๋ธ” ์ด๋ฆ„์œผ๋กœ ๋งคํ•‘ํ•œ๋‹ค. (์ •ํ™•ํžˆ๋Š” ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•จ)
@Id ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์˜ ํ•„๋“œ๋ฅผ ํ…Œ์ด๋ธ” ๊ธฐ๋ณธํ‚ค(primary key)์— ๋งคํ•‘ํ•œ๋‹ค. @Id๊ฐ€ ์‚ฌ์šฉ๋œ ํ•„๋“œ๋ฅผ ์‹๋ณ„์ž ํ•„๋“œ๋ผ ํ•œ๋‹ค.
@Column ํ•„๋“œ๋ฅผ ์ปฌ๋Ÿผ์— ๋งคํ•‘ํ•œ๋‹ค.
๋งคํ•‘ ์ •๋ณด๊ฐ€ ์—†๋Š” ํ•„๋“œ ํ•„๋“œ๋ช…์„ ์‚ฌ์šฉํ•ด์„œ ์ปฌ๋Ÿผ๋ช…์œผ๋กœ ๋งคํ•‘ํ•œ๋‹ค. ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด @Column(name=โ€AGEโ€)์ฒ˜๋Ÿผ ๋ช…์‹œ์ ์œผ๋กœ ๋งคํ•‘ํ•ด์•ผ ํ•œ๋‹ค.



2.5 ๊ฐ์ฒด ๋งคํ•‘ ์‹œ์ž‘

2.5.1 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฉ์–ธ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งˆ๋‹ค SQL ๋ฌธ๋ฒ•๊ณผ ํ•จ์ˆ˜๊ฐ€ ์กฐ๊ธˆ์”ฉ ๋‹ค๋ฅด๋‹ค๋Š” ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค.

  • ๋ฐ์ดํ„ฐ ํƒ€์ž…: ๊ฐ€๋ณ€ ๋ฌธ์ž ํƒ€์ž…์œผ๋กœ MySQL์€ VARCHAR, ์˜ค๋ผํด์€ VARCHAR2 ์‚ฌ์šฉ
  • ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ช…: ๋ฌธ์ž์—ด์„ ์ž๋ฅด๋Š” ํ•จ์ˆ˜๋กœ SQL ํ‘œ์ค€์€ SUBSTRING(), ์˜ค๋ผํด์€ SUBSTR()
  • ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ: MySQL์€ LIMIT, ์˜ค๋ผํด์€ ROWNUM

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ์˜ ๊ณ ์œ ํ•œ ๊ธฐ๋Šฅ์„ JPA์—์„œ๋Š” ๋ฐฉ์–ธ(Dialect)์ด๋ผ ํ•œ๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ํฌํ•จํ•œ ๋Œ€๋ถ€๋ถ„์˜ JPA ๊ตฌํ˜„์ฒด๋“ค์€ ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฉ์–ธ ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋ฐฉ์–ธ๋งŒ ๊ต์ฒดํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋งž๋Š” SQL์ด ์•Œ์•„์„œ ์‹คํ–‰๋œ๋‹ค.

๋ฐฉ์–ธ
<!-- ํ•„์ˆ˜ ์†์„ฑ -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <!-- ๋ฐฉ์–ธ ์„ค์ • -->

<!-- ์˜ต์…˜ -->
<property name="hibernate.show_sql" value="true" /> <!-- ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์‹คํ–‰ํ•œ SQL์„ ์ถœ๋ ฅํ•œ๋‹ค -->
<property name="hibernate.format_sql" value="true" /> <!-- ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์‹คํ–‰ํ•œ SQL์„ ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ์ •๋ ฌํ•œ๋‹ค -->
<property name="hibernate.use_sql_comments" value="true" /> <!-- ์ฟผ๋ฆฌ๋ฅผ ์ถœ๋ ฅํ•  ๋•Œ ์ฃผ์„๋„ ํ•จ๊ป˜ ์ถœ๋ ฅํ•œ๋‹ค -->
<property name="hibernate.id.new_generator_mappings" value="true" /> <!-- JPA ํ‘œ์ค€์— ๋งž์ถ˜ ์ƒˆ๋กœ์šด ํ‚ค ์ƒ์„ฑ ์ „๋žต์„ ์‚ฌ์šฉํ•œ๋‹ค. -->




2.6 ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ

์‹œ์ž‘์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž

public class JpaMain {

    public static void main(String[] args) {

        //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ ์ƒ์„ฑ
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
        EntityManager em = emf.createEntityManager(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ƒ์„ฑ

        EntityTransaction tx = em.getTransaction(); //ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ ํš๋“

        try {
            tx.begin(); //ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
            logic(em);  //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
            tx.commit();//ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback(); //ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
        } finally {
            em.close(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ข…๋ฃŒ
        }

        emf.close(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ ์ข…๋ฃŒ
    }

    // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
    public static void logic(EntityManager em) {...}
}

์ฝ”๋“œ๋Š” ํฌ๊ฒŒ 3๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰˜์–ด ์žˆ๋‹ค

  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ƒ์„ฑ
  • ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง


2.6.1 ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์„ค์ •

์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ ์ƒ์„ฑ
JPA๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ์šฐ์„  ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");

์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ๋Š” JPA๋ฅผ ๋™์ž‘์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๊ธฐ๋ฐ˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ํ’€๋„ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์ƒ์„ฑ ๋น„์šฉ์€ ์•„์ฃผ ํฌ๋‹ค. ๋”ฐ๋ผ์„œ ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์— ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑํ•˜๊ณ  ๊ณต์œ ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ƒ์„ฑ

EntityManager em = emf.createEntityManager();

JPA์˜ ๊ธฐ๋Šฅ ๋Œ€๋ถ€๋ถ„์€ ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๊ฐ€ ์ œ๊ณตํ•œ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋“ฑ๋ก,์ˆ˜์ •,์‚ญ์ œ,์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜๊ณผ ๋ฐ€์ ‘ํ•œ ๊ด€๊ณ„๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์Šค๋ ˆ๋“œ๊ฐ„์— ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์žฌ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

์ข…๋ฃŒ
์‚ฌ์šฉ์ด ๋๋‚œ ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €, ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ๋Š” ๋ฐ˜๋“œ์‹œ ์ข…๋ฃŒํ•ด์•ผํ•œ๋‹ค.

em.close(); // ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ข…๋ฃŒ
emf.close() // ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ ์ข…๋ฃŒ




2.6.2 ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ

JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ญ์ƒ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค. ํŠธ๋žœ์žญ์…˜ ์—†์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

EntityTransaction tx = em.getTransaction(); //ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ ํš๋“
try {
    tx.begin(); //ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
    logic(em);  //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
    tx.commit();//ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

} catch (Exception e) {
    e.printStackTrace();
    tx.rollback(); //ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
} finally {
    em.close(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ์ข…๋ฃŒ
}

emf.close(); //์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ ์ข…๋ฃŒ




2.6.3 ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง

JPA๋ฅผ ์ด์šฉํ•œ ๋“ฑ๋ก, ์ˆ˜์ •, ์กฐํšŒ, ์‚ญ์ œ ์˜ˆ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

public static void logic(EntityManager em) {

        String id = "id1";
        Member member = new Member();
        member.setId(id);
        member.setUsername("์ง€ํ•œ");
        member.setAge(2);

        //๋“ฑ๋ก
        // INSERT INTO MEMBER ...
        em.persist(member);

        //์ˆ˜์ •
        // JPA๋Š” ์—”ํ‹ฐํ‹ฐ ๋ณ€๊ฒฝ ์ถ”์  ๊ธฐ๋Šฅ์ด ์žˆ์–ด ์—”ํ‹ฐํ‹ฐ์˜ ๊ฐ’๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ์ˆ˜์ •๋จ
        // UPDATE MEMBER SET AGE=20, NAME='์ง€ํ•œ' WHERE ID = 'id1';
        member.setAge(20);

        //ํ•œ ๊ฑด ์กฐํšŒ
        // SELECT * FROM MEMBER WHERE ID = 'id1';
        Member findMember = em.find(Member.class, id);
        System.out.println("findMember=" + findMember.getUsername() + ", age=" + findMember.getAge());

        //๋ชฉ๋ก ์กฐํšŒ
        List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
        System.out.println("members.size=" + members.size());

        //์‚ญ์ œ
        // DELETE FROM MEMBER WHERE ID = 'id1';
        em.remove(member);
    }




2.6.4 JPQL

JPA๋Š” SQL์„ ์ถ”์ƒํ™”ํ•œ JPQL์ด๋ผ๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

//๋ชฉ๋ก ์กฐํšŒ
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
System.out.println("members.size=" + members.size());

JPQL๊ณผ SQL์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ 

  • JPQL์€ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌํ•œ๋‹ค.
  • SQL์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌํ•œ๋‹ค.

JPQL์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ์ „ํ˜€ ์•Œ์ง€ ๋ชปํ•œ๋‹ค.



2.7 ์ •๋ฆฌ

  • JPA๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •
  • JPA๋ฅผ ์‚ฌ์šฉ
  • JPQL ์„ค๋ช…




3. ์˜์†์„ฑ ๊ด€๋ฆฌ

3.1 ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ์™€ ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €

  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ๋น„์šฉ์€ ์ƒ๋‹นํžˆ ํฌ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ ๊ณต์œ ํ•˜๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ๋‹ค. (๊ทธ๋ž˜์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑ ํ•œ๋‹ค)
  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ๋Š” ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•ด๋„ ์•ˆ์ „ํ•˜๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•˜๋ฉด ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
์ผ๋ฐ˜์ ์ธ์›น์• ํ”Œ๋ฆฌ์ผ€์ด์…˜



3.2 ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ž€?

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ(persistence context)๋ž€ ํ•ด์„ํ•˜์ž๋ฉด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ์ด๋ž€ ๋œป์ด๋‹ค.

// ์ด ๋ฉ”์†Œ๋“œ๋Š” ์ •ํ™•ํžˆ ํ‘œํ˜„ํ•˜์ž๋ฉด '์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํšŒ์› ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•œ๋‹ค' ์ด๋‹ค.
em.persist(memeber);

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š”

  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์ง„๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌ๋ฐ›๋Š”๋‹ค.
  • ์—ฌ๋Ÿฌ ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜๋„ ์žˆ๋‹ค.



3.3 ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ

์—”ํ‹ฐํ‹ฐ์—๋Š” 4๊ฐ€์ง€ ์ƒํƒœ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

  • ๋น„์˜์†(new / transient): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒํƒœ
  • ์˜์†(managed): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋œ ์ƒํƒœ
  • ์ค€์˜์†(detached): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋˜์—ˆ๋‹ค๊ฐ€ ๋ถ„๋ฆฌ๋œ ์ƒํƒœ
  • ์‚ญ์ œ(removed): ์‚ญ์ œ๋œ ์ƒํƒœ
์ƒ๋ช…์ฃผ๊ธฐ




3.4 ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ํŠน์ง•

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ํŠน์ง•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹๋ณ„์ž๊ฐ’(@Id)์œผ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค. ์˜์† ์ƒํƒœ๋Š” ์‹๋ณ„์ž ๊ฐ’์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • JPA๋Š” ๋ณดํ†ต ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ƒˆ๋กœ ์ €์žฅ๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•˜๋Š”๋ฐ ์ด๊ฒƒ์„ ํ”Œ๋Ÿฌ์‹œ(flush)๋ผ ํ•œ๋‹ค.

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ ์žฅ์ 

  • 1์ฐจ ์บ์‹œ
  • ๋™์ผ์„ฑ ๋ณด์žฅ
  • ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ
  • ๋ณ€๊ฒฝ ๊ฐ์ง€
  • ์ง€์—ฐ๋กœ๋”ฉ



3.4.1 ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ๋‚ด๋ถ€์— ์บ์‹œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ ์ด๊ฒƒ์„ 1์ฐจ ์บ์‹œ๋ผ ํ•œ๋‹ค. ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋Š” ๋ชจ๋‘ ์ด๊ณณ์— ์ €์žฅ๋œ๋‹ค.

1์ฐจ ์บ์‹œ์—์„œ ์กฐํšŒ

1์ฐจ์บ์‹œ์—์„œ์กฐํšŒ
  • 1์ฐจ ์บ์‹œ์˜ ํ‚ค๋Š” ์‹๋ณ„์ž(@Id) ๊ฐ’.
  • ์‹๋ณ„์ž ๊ฐ’์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ณธ ํ‚ค์™€ ๋งคํ•‘.
  • 1์ฐจ ์บ์‹œ์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐพ๊ณ  ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์—†์œผ๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ๋‹ค.
// ๋น„์˜์† ์ƒํƒœ
Member member = new Member();
member.setId("member1");
member.setUsername("ํšŒ์›1");

// ์˜์† ์ƒํƒœ (1์ฐจ ์บ์‹œ์— ์ €์žฅ๋จ)
em.persist(member);

// 1์ฐจ ์บ์‹œ์—์„œ ์กฐํšŒ
Member findMember = em.find(Member.class, "member1");



๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒ

  • ์กฐํšŒ์‹œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ 1์ฐจ ์บ์‹œ์— ์—†์œผ๋ฉด ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•ด์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑ.
  • 1์ฐจ ์บ์‹œ์— ์ €์žฅํ•œ ํ›„์— ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
1์ฐจ์บ์‹œ์—์—†์œผ๋ฉด๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์„œ์กฐํšŒ



์˜์† ์—”ํ‹ฐํ‹ฐ์˜ ๋™์ผ์„ฑ ๋ณด์žฅ
์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์„ฑ๋Šฅ์ƒ ์ด์ ๊ณผ ์—”ํ‹ฐํ‹ฐ์˜ ๋™์ผ์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b);

๋™์ผ์„ฑ๊ณผ ๋™๋“ฑ์„ฑ

  • ๋™์ผ์„ฑ(identity): ์‹ค์ œ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ฐ™๋‹ค. ๋”ฐ๋ผ์„œ ์ฐธ์กฐ ๊ฐ’์„ ๋น„๊ตํ•˜๋Š” == ๋น„๊ต์˜ ๊ฐ’์ด ๊ฐ™๋‹ค.
  • ๋™๋“ฑ์„ฑ(equality): ์‹ค์ œ ์ธ์Šคํ„ด์Šค๋Š” ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์ง€๋งŒ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ’์ด ๊ฐ™๋‹ค.



3.4.2 ์—”ํ‹ฐํ‹ฐ ๋“ฑ๋ก

EntityManager em = emf.createEntityManager(); 
EntityTransaction tx = em.getTransaction();
// ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
tx.begin(); //ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘

em.persist(memberA);
em.persist(memberB);
// ์—ฌ๊ธฐ๊นŒ์ง€ INSERT SQL์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.

// ์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— INSERT SQL์„ ๋ณด๋‚ธ๋‹ค.
tx.commit();
  1. ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๊ธฐ ์ง์ „๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ๋‚ด๋ถ€ ์ฟผ๋ฆฌ ์ €์žฅ์†Œ์— INSERT SQL์„ ๋ชจ์•„๋‘”๋‹ค.
  2. ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๋•Œ ๋ชจ์•„๋‘” ์ฟผ๋ฆฌ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณด๋‚ธ๋‹ค. ์ด๋ฅผ ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ์ด๋ผ ํ•œ๋‹ค.
์“ฐ๊ธฐ์ง€์—ฐ


์“ฐ๊ธฐ์ง€์—ฐ


์“ฐ๊ธฐ์ง€์—ฐ

ํ”Œ๋Ÿฌ์‹œ

  • ํ”Œ๋Ÿฌ์‹œ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋™๊ธฐํ™” ํ•˜๋Š” ์ž‘์—…
  • ๊ตฌ์ฒด์ ์œผ๋กœ ์ด์•ผ๊ธฐํ•˜๋ฉด ์“ฐ๊ธฐ ์ง€์—ฐ SQL์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณด๋‚ด๋Š” ์ž‘์—…์ด๋‹ค.

ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ์ด ๊ฐ€๋Šฅํ•œ ์ด์œ 

  • ๋“ฑ๋ก ์ฟผ๋ฆฌ๋ฅผ ๊ทธ๋•Œ ๊ทธ๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „๋‹ฌํ•ด๋„ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์œผ๋ฉด ์•„๋ฌด ์†Œ์šฉ ์—†๋‹ค.
  • ๊ฒฐ๊ตญ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— SQL์„ ๊ทธ๋•Œ ๊ทธ๋•Œ ์ „๋‹ฌํ•˜๋‚˜, ์ปค๋ฐ‹ ์ง์ „์—๋งŒ ์ „๋‹ฌํ•˜๋‚˜ ๊ฒฐ๊ณผ๋Š” ๊ฐ™๋‹ค.



3.4.3 ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ •

SQL ์ˆ˜์ • ์ฟผ๋ฆฌ์˜ ๋ฌธ์ œ์ 

  • ์ˆ˜์ • ์ฟผ๋ฆฌ๊ฐ€ ๋งŽ์•„์ง„๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด SQL์„ ๊ณ„์† ํ™•์ธํ•ด์•ผํ•œ๋‹ค.
  • ๊ฒฐ๊ตญ ์ง๊ฐ„์ ‘์ ์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด SQL์— ์˜์กดํ•œ๋‹ค.

JPA์˜ ์ˆ˜์ •: ๋ณ€๊ฒฝ๊ฐ์ง€

  • JPA์—์„œ ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ •์€ ๋‹จ์ˆœํžˆ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•ด์„œ ๋ฐ์ดํ„ฐ๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋œ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ž๋™์œผ๋กœ ๋ฐ˜์˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋ณ€๊ฒฝ ๊ฐ์ง€๋ผ ํ•œ๋‹ค.
  • ๋ณ€๊ฒฝ ๊ฐ์ง€๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ์—๋งŒ ์ ์šฉ๋œ๋‹ค.
EntityManager em = emf.createEntityManager(); 
EntityTransaction tx = em.getTransaction();

// ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
tx.begin();

// ์˜์† ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ
Member memberA = em.find(Member.class, "memberA");

// ์˜์† ์—”ํ‹ฐํ‹ฐ ๋ฐ์ดํ„ฐ ์ˆ˜์ •
memberA.setUserName("hi");
memberA.setAge(10);

// ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹
tx.commit();
๋ณ€๊ฒฝ๊ฐ์ง€


  • JPA์˜ ์ˆ˜์ • ๊ธฐ๋ณธ์ „๋žต์€ ์—”ํ‹ฐํ‹ฐ์˜ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค.
  • ํ•„๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ๊ฑฐ๋‚˜ ์ €์žฅ๋˜๋Š” ๋‚ด์šฉ์ด ๋„ˆ๋ฌด ํฌ๋ฉด ์ˆ˜์ •๋œ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉํ•ด์„œ ๋™์ ์œผ๋กœ UPDATE SQL์„ ์ƒ์„ฑํ•˜๋Š” ์ „๋žต์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.
@Entity
@org.hibernate.annotations.DynamicUpdate
public class Member{...}

์ฐธ๊ณ 
์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒ ์ง€๋งŒ ์ปฌ๋Ÿผ์ด ๋Œ€๋žต 30๊ฐœ ์ •๋„๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๊ธฐ๋ณธ ์ „๋žต์˜ ์ˆ˜์ • ์ฟผ๋ฆฌ๊ฐ€ ๋น ๋ฅด๋‹ค.
์ถ”๊ฐ€๋กœ INSERT SQL์„ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” @DynamicInsert๋„ ์žˆ๋‹ค.



3.4.4 ์—”ํ‹ฐํ‹ฐ ์‚ญ์ œ

// ์˜์† ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA);
  1. em.remove(); ํ˜ธ์ถœ
  2. ํ˜ธ์ถœ ์ˆœ๊ฐ„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ์—”ํ‹ฐํ‹ฐ ์ œ๊ฑฐ
  3. ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ์‚ญ์ œ ์ฟผ๋ฆฌ ๋“ฑ๋ก
  4. ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹์‹œ ํ”Œ๋Ÿฌ์‹œ (์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์‚ญ์ œ ์ฟผ๋ฆฌ ์ „๋‹ฌ)
  5. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹


3.5 ํ”Œ๋Ÿฌ์‹œ

ํ”Œ๋Ÿฌ์‹œ๋Š”(flush())๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•œ๋‹ค. ๋™์ž‘ ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ๋ณ€๊ฒฝ ๊ฐ์ง€๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์žˆ๋Š” ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์Šค๋ƒ…์ƒท๊ณผ ๋น„๊ตํ•ด์„œ ์ˆ˜์ •๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐพ๋Š”๋‹ค.
  2. ์ˆ˜์ •๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ์ˆ˜์ • ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ๋“ฑ๋กํ•œ๋‹ค.
  3. ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์˜ ์ฟผ๋ฆฌ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „์†กํ•œ๋‹ค.(๋“ฑ๋ก,์ˆ˜์ •,์‚ญ์ œ ์ฟผ๋ฆฌ)

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”Œ๋Ÿฌ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ• 3๊ฐ€์ง€

  1. ์ง์ ‘ ํ˜ธ์ถœ (em.flush())
  2. ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹
  3. JPQL ์ฟผ๋ฆฌ ์‹คํ–‰
    • JPQL์€ SQL๋กœ ๋ณ€ํ™˜๋˜์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ
    • ๋งŒ์•ฝ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—†์œผ๋ฉด ์กฐํšŒ ๋ถˆ๊ฐ€
    • ๋”ฐ๋ผ์„œ ํ”Œ๋Ÿฌ์‹œ๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•ด์•ผ ํ•จ

์ฐธ๊ณ 
์‹๋ณ„์ž๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์กฐํšŒํ•˜๋Š” find() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ํ”Œ๋Ÿฌ์‹œ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.



3.5.1 ํ”Œ๋Ÿฌ์‹œ ๋ชจ๋“œ ์˜ต์…˜

  • FlushModeType.AUTO: ์ปค๋ฐ‹์ด๋‚˜ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ํ”Œ๋Ÿฌ์‹œ(๊ธฐ๋ณธ๊ฐ’)
  • FlushModeType.COMMIT: ์ปค๋ฐ‹ํ•  ๋•Œ๋งŒ ํ”Œ๋Ÿฌ์‹œ
em.setFlushMode(FlushModeType.COMMIT); // ํ”Œ๋Ÿฌ์‹œ ๋ชจ๋“œ ์ง์ ‘ ์„ค์ •




3.6 ์ค€์˜์†

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ถ„๋ฆฌ๋œ ๊ฒƒ์„ ์ค€์˜์† ์ƒํƒœ๋ผ ํ•œ๋‹ค.
  • ์ค€์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ค€์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

  1. em.detach(entity): ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋งŒ ์ค€์˜์† ์ƒํƒœ๋กœ ์ „ํ™˜
  2. em.clear(): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”
  3. em.close(): ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ข…๋ฃŒ


3.6.1 ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ค€์˜์† ์ƒํƒœ๋กœ ์ „ํ™˜: detach()

ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋งŒ ์ค€์˜์† ์ƒํƒœ๋กœ ์ „ํ™˜


3.6.2 ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ดˆ๊ธฐํ™”: clear()

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์„œ ํ•ด๋‹น ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ค€์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ฌ


3.6.3 ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ข…๋ฃŒ: close()

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ข…๋ฃŒํ•˜๋ฉด ํ•ด๋‹น ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋˜ ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ชจ๋‘” ์ค€์˜์† ์ƒํƒœ๊ฐ€ ๋œ๋‹ค.


3.6.4 ์ค€์˜์† ์ƒํƒœ์˜ ํŠน์ง•

  • ๊ฑฐ์˜ ๋น„์˜์† ์ƒํƒœ์— ๊ฐ€๊น๋‹ค.
  • ์‹๋ณ„์ž ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. (์ค€์˜์† ์ƒํƒœ๋Š” ์ด๋ฏธ ํ•œ ๋ฒˆ ์˜์† ์ƒํƒœ์˜€์œผ๋ฏ€๋กœ ์‹๋ณ„์ž ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค)
  • ์ง€์—ฐ ๋กœ๋”ฉ์„ ํ•  ์ˆ˜ ์—†๋‹ค.


3.6.5 ๋ณ‘ํ•ฉ: merge()

์ค€์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค์‹œ ์˜์† ์ƒํƒœ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด ๋ณ‘ํ•ฉ์„ ์‚ฌ์šฉํ•œ๋‹ค. merge() ๋ฉ”์†Œ๋“œ๋Š” ์ค€์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ›์•„์„œ ๊ทธ ์ •๋ณด๋กœ ์ƒˆ๋กœ์šด ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Member mergeMember = em.merge(member);


์ค€์˜์† ๋ณ‘ํ•ฉ
์ค€์˜์† ์ƒํƒœ์ธ member์—”ํ‹ฐํ‹ฐ์™€ ์˜์† ์ƒํƒœ์ธ mergeMember ์—”ํ‹ฐํ‹ฐ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฐธ์กฐํ•˜๋„๋ก ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค.

// Member mergeMember = em.merge(member); // ์ด ์ฝ”๋“œ๋Š” ์œ„ํ—˜
member = em2.merge(member); // ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉ


๋น„์˜์† ๋ณ‘ํ•ฉ
๋ณ‘ํ•ฉ์€ ๋น„์˜์† ์—”ํ‹ฐํ‹ฐ๋„ ์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

Member member = new Member();
Mmeber newMember = em.merge(member); // ๋น„์˜์† ๋ณ‘ํ•ฉ
tx.commit();


๋ณ‘ํ•ฉ ๋™์ž‘ ์ˆœ์„œ

  1. ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ ์—”ํ‹ฐํ‹ฐ์˜ ์‹๋ณ„์ž ๊ฐ’์œผ๋กœ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์กฐํšŒ
  2. ์ฐพ๋Š” ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์—†์œผ๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ
  3. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ๋„ ์—†์œผ๋ฉด ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑํ•ด์„œ ๋ณ‘ํ•ฉ


3.7 ์ •๋ฆฌ

  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ์•คํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ € ํŒฉํ† ๋ฆฌ์—์„œ ์ƒ์„ฑํ•œ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์ด์—์„œ ๊ฐ์ฒด๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ๊ฐ€์ƒ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ™์€ ์—ญํ• ์„ ํ•œ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๋•๋ถ„์— 1์ฐจ ์บ์‹œ, ๋™์ผ์„ฑ ๋ณด์žฅ, ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ์ง€์—ฐ, ๋ณ€๊ฒฝ ๊ฐ์ง€, ์ง€์—ฐ ๋กœ๋”ฉ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ํ”Œ๋Ÿฌ์‹œ ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜๋œ๋‹ค.
  • ํ”Œ๋Ÿฌ์‹œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๋•Œ ๋™์ž‘ํ•œ๋‹ค.
  • ์˜์† ์ƒํƒœ: ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ
  • ์ค€์˜์† ์ƒํƒœ: ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ถ„๋ฆฌ๋œ ๊ฒƒ์„ ์ค€์˜์† ์ƒํƒœ





4. ์—”ํ‹ฐํ‹ฐ ๋งคํ•‘

  • ๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ” ๋งคํ•‘: @Entity, @Table
  • ๊ธฐ๋ณธ ํ‚ค ๋งคํ•‘: @Id
  • ํ•„๋“œ์™€ ์ปฌ๋Ÿผ ๋งคํ•‘: @Column
  • ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘: @ManyToOne, @JoinColumn


4.1 @Entity

JPA๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘ํ•  ํด๋ž˜์Šค

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
name JPA์—์„œ ์‚ฌ์šฉํ•  ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ์ง€์ •ํ•œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ ํด๋ž˜์Šค ์ด๋ฆ„์ด๋‹ค. ๋งŒ์•ฝ ๋‹ค๋ฅธ ํŒจํ‚ค์ง€์— ์ด๋ฆ„์ด ๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๋ฉด ์ถœ๋Œํ•œ๋‹ค. ํด๋ž˜์Šค์ด๋ฆ„ (์˜ˆ: Member)

์ฃผ์˜์‚ฌํ•ญ

  • ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋Š” ํ•„์ˆ˜ (public ๋˜๋Š” protected)
  • final, enum, interface, inner ํด๋ž˜์Šค์—๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  • ์ €์žฅํ•  ํ•„๋“œ์— final์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.



4.2 @Table

์—”ํ‹ฐํ‹ฐ์™€ ๋งคํ•‘ํ•  ํ…Œ์ด๋ธ”์„ ์ง€์ •. ์ƒ๋žตํ•˜๋ฉด ๋งคํ•‘ํ•œ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ํ…Œ์ด๋ธ” ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
name ๋งคํ•‘ํ•  ํ…Œ์ด๋ธ” ์ด๋ฆ„ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•œ๋‹ค.
catalog catalog ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ catalog๋ฅผ ๋งคํ•‘ํ•œ๋‹ค. ย 
schema schema ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ schema๋ฅผ ๋งคํ•‘ํ•œ๋‹ค. ย 
uniqueConstraints DDL ์ƒ์„ฑ ์‹œ ์œ ๋‹ˆํฌ ์ œ์•ฝ์กฐ๊ฑด์„ ๋งŒ๋“ ๋‹ค. 2๊ฐœ ์ด์ƒ์˜ ๋ณตํ•ฉ ์œ ๋‹ˆํฌ ์ œ์•ฝ์กฐ๊ฑด๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ DDL์„ ๋งŒ๋“ค ๋•Œ๋งŒ ์‚ฌ์šฉ๋œ๋‹ค. ย 



4.3 ๋‹ค์–‘ํ•œ ๋งคํ•‘ ์‚ฌ์šฉ

@Entity
@Table(name="MEMBER",
    uniqueConstraints = {@UniqueConstraint(
        name = "NAME_AGE_UNIQUE",
        columnNames = {"NAME", "AGE"} 
    )}
)
public class Member {
    ...์ƒ๋žต...

    // enum์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @Enumerated ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋งคํ•‘
    @Enumerated(EnumType.STRING)
    private RoleType roleType;

    // ์ž๋ฐ”์˜ ๋‚ ์งœ ํƒ€์ž…์€ @Temporal ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋งคํ•‘
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    // CLOB, BLOBํƒ€์ž…์€ @Lob ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋งคํ•‘
    @Lob
    private String description;

    ...์ƒ๋žต...
}



4.4 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑ

JPA๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•œ๋‹ค.

<!-- ์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑ -->
<property name="hibernate.hbm2ddl.auto" value="create" />

<!-- ์ฝ˜์†”์— ํ…Œ์ด๋ธ” ์ƒ์„ฑ DDL ์ถœ๋ ฅ -->
<property name="hibernate.show_sql" value="true" />


hibernate.hbm2ddl.auto ์†์„ฑ

์˜ต์…˜ ์„ค๋ช…
create ๊ธฐ์กด ํ…Œ์ด๋ธ”์„ ์‚ญ์ œํ•˜๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑํ•œ๋‹ค. DROP + CREATE
create-drop create ์†์„ฑ์— ์ถ”๊ฐ€๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ ์ƒ์„ฑํ•œ DDL์„ ์ œ๊ฑฐํ•œ๋‹ค. DROP + CREATE + DROP
update ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ์—”ํ‹ฐํ‹ฐ ๋งคํ•‘์ •๋ณด๋ฅผ ๋น„๊ตํ•ด์„œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋งŒ ์ˆ˜์ •ํ•œ๋‹ค.
validate ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ์—”ํ‹ฐํ‹ฐ ๋งคํ•‘์ •๋ณด๋ฅผ ๋น„๊ตํ•ด์„œ ์ฐจ์ด๊ฐ€ ์žˆ์œผ๋ฉด ๊ฒฝ๊ณ ๋ฅผ ๋‚จ๊ธฐ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค.
none ์ž๋™ ์ƒ์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ ค๋ฉด hibernate.hbm2ddl.auto ์†์„ฑ ์ž์ฒด๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์œ ํšจํ•˜์ง€ ์•Š์€ ์˜ต์…˜ ๊ฐ’์„ ์ฃผ๋ฉด ๋œ๋‹ค.

hibernate.hbm2ddl.auto ์ฃผ์˜์‚ฌํ•ญ
์šด์˜ ์„œ๋ฒ„์—์„œ๋Š” DDL์„ ์ˆ˜์ •ํ•˜๋Š” ์˜ต์…˜์€ ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

  • ๊ฐœ๋ฐœ ์ดˆ๊ธฐ ๋‹จ๊ณ„: create, update
  • ์ดˆ๊ธฐํ™” ์ƒํƒœ๋กœ ํ…Œ์ŠคํŠธ, CI์„œ๋ฒ„: create, create-drop
  • ํ…Œ์ŠคํŠธ ์„œ๋ฒ„: update, validate
  • ์Šคํ…Œ์ด์ง•, ์šด์˜ ์„œ๋ฒ„: validate, none



๊ธฐ๋ณธ ์ด๋ฆ„ ๋งคํ•‘ ์ „๋žต ๋ณ€๊ฒฝํ•˜๊ธฐ
ํ…Œ์ด๋ธ” ๋ช…์ด๋‚˜ ์ปฌ๋Ÿผ ๋ช…์ด ์ƒ๋žต๋˜๋ฉด ์ž๋ฐ”์˜ ์นด๋ฉœ ํ‘œ๊ธฐ๋ฒ•์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์˜ ์–ธ๋”์Šค์ฝ”์–ด ํ‘œ๊ธฐ๋ฒ•์œผ๋กœ ๋งคํ•‘ํ•œ๋‹ค.

<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />




4.5 DDL ์ƒ์„ฑ ๊ธฐ๋Šฅ

์ œ์•ฝ ์กฐ๊ฑด ์ถ”๊ฐ€
์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ๋ฅผ ํ†ตํ•ด DDL์— ์ œ์•ฝ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ DDL์„ ์ž๋™ ์ƒ์„ฑํ•  ๋•Œ๋งŒ ์‚ฌ์šฉ๋˜๊ณ  JPA์˜ ์‹คํ–‰ ๋กœ์ง์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.

@Entity
@Table(name="MEMBER", 
    /** 
     *  ์œ ๋‹ˆํฌ ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€
     *  ALET TABLE MEMBER
     *      ADD CONSTRAINT NAME_AGE_UNIQUE UNIQUE (NAME, AGE)
     */
    uniqueConstraints = {@UniqueConstraint(
        name = "NAME_AGE_UNIQUE",
        columnNames = {"NAME", "AGE"} 
    )}
)
public class Member {
    ...์ƒ๋žต...

    // ์ปฌ๋Ÿผ๋ช… NAME, not null, ๋ฌธ์žํฌ๊ธฐ 10
    @Column(name = "NAME", nullable = false, length = 10)
    private String username;

    ...์ƒ๋žต...
}



4.6 DDL ์ƒ์„ฑ ๊ธฐ๋Šฅ

JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ณธ ํ‚ค ์ƒ์„ฑ ์ „๋žต์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ง์ ‘ ํ• ๋‹น: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ง์ ‘ ํ• ๋‹น
  • ์ž๋™ ์ƒ์„ฑ: ๋Œ€๋ฆฌ ํ‚ค ์‚ฌ์šฉ ๋ฐฉ์‹
    • IDENTITY: ๊ธฐ๋ณธ ํ‚ค ์ƒ์„ฑ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์œ„์ž„
    • SEQUENCE: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค๋ฅผ ์‚ฌ์šฉ
    • TABLE: ํ‚ค ์ƒ์„ฑ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉ

์ž๋™ ์ƒ์„ฑ ์ „๋žต์ด ๋‹ค์–‘ํ•œ ์ด์œ ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฒค๋”๋งˆ๋‹ค ์ง€์›ํ•˜๋Š” ๋ฐฉ์‹์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ‚ค ์ƒ์„ฑ ์ „๋žต์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋‹ค์Œ ์†์„ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

<property name="hibernate.id.new_generator_mappings" value="true" />



4.6.1 ๊ธฐ๋ณธ ํ‚ค ์ง์ ‘ ํ• ๋‹น ์ „๋žต

@Id
@Column(name = "ID")
private String id;
Board board = new Board();
board.setId("id"); // ๊ธฐ๋ณธ ํ‚ค ์ง์ ‘ ํ• ๋‹น
em.persist(board);



4.6.2 IDENTITY ์ „๋žต

  • IDENTITY๋Š” ๊ธฐ๋ณธ ํ‚ค ์ƒ์„ฑ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์œ„์ž„ํ•˜๋Š” ์ „๋žต์ด๋‹ค.
  • IDENTITY ์‹๋ณ„์ž ์ƒ์„ฑ ์ „๋žต์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•ด์•ผ ์‹๋ณ„์ž๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • em.persist()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฆ‰์‹œ INSERT SQL์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „๋‹ฌ๋œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ์ด ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Board board = new Board();
em.persist(board);
board.getId(); // 1

์ฐธ๊ณ 
IDENTITY ์ „๋žต์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— INSERTํ•œ ํ›„์— ๊ธฐ๋ณธ ํ‚ค๊ฐ’์„ ์กฐํšŒํ•  ์ˆ˜์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์—”ํ‹ฐํ‹ฐ์— ์‹๋ณ„์ž ๊ฐ’์„ ํ• ๋‹นํ•˜๋ ค๋ฉด JPA๋Š” ์ถ”๊ฐ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” JDBC3์— ์ถ”๊ฐ€๋œ Statement.getGeneratedKeys() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํ•œ๋ฒˆ๋งŒ ํ†ต์‹ ํ•œ๋‹ค.



4.6.3 SEQUENCE ์ „๋žต

SEQUENCE ์ „๋žต์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค๋ฅผ ์ด์šฉํ•ด ๊ธฐ๋ณธ ํ‚ค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

-- ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค ์ƒ์„ฑ
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
@Entity
@SequenceGenerator(
    name = "BOARD_SEQ_GENERATOR",
    sequenceName = "BOARD_SEQ", // ๋งคํ•‘ํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค ์ด๋ฆ„
    initialValue = 1,
    allocationsSize = 1
)
public class BOARD {

    @Id
    @GeneratedValue(
        strategy = GenerationType.SEQUENCE,
        generator = "BOARD_SEQ_GENERATOR"
    )
    private Long id;
}


@SequenceGenerator

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
name ์‹๋ณ„์ž ์ƒ์„ฑ๊ธฐ ์ด๋ฆ„ ํ•„์ˆ˜
sequenceName ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋“ฑ๋ก๋˜์–ด ์žˆ๋Š” ์‹œํ€€์Šค ์ด๋ฆ„ hibernate_sequence
initialValue DDL ์ƒ์„ฑ ์‹œ์—๋งŒ ์‚ฌ์šฉ๋จ. ์‹œํ€€์Šค DDL์„ ์ƒ์„ฑํ•  ๋•Œ ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” ์ˆ˜๋ฅผ ์ง€์ •ํ•œ๋‹ค. 1
allocationsSize ์‹œํ€€์Šค ํ•œ ๋ฒˆ ํ˜ธ์ถœ์— ์ฆ๊ฐ€ํ•˜๋Š” ์ˆ˜ (์„ฑ๋Šฅ ์ตœ์ ํ™”์— ์‚ฌ์šฉ๋จ) 50
catalog, schema ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค catalog, schema ์ด๋ฆ„ ย 


SEQUENCE ์ „๋žต๊ณผ ์ตœ์ ํ™”
SEQUENCE ์ „๋žต์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ 2๋ฒˆ ํ†ต์‹ ํ•œ๋‹ค.

  1. ์‹๋ณ„์ž ๊ตฌํ•˜๊ธฐ (SELECT BOARD_SEQ.NEXTVAL FROM DUAL)
  2. ์กฐํšŒํ•œ ์‹œํ€€์Šค๋ฅผ ๊ธฐ๋ณธ ํ‚ค ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ (INSERT INTO BOARD โ€ฆ)

JPA๋Š” ์‹œํ€€์Šค ์ ‘๊ทผํ•˜๋Š” ํšŸ์ˆ˜๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด @SequenceGenerator.allocationsSize๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์—ฌ๊ธฐ์— ์„ค์ •ํ•œ ๊ฐ’๋งŒํผ ํ•œ ๋ฒˆ์— ์‹œํ€€์Šค ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค๊ณ , ๊ทธ๋งŒํผ ๋ฉ”๋ชจ๋ฆฌ์— ์‹œํ€€์Šค ๊ฐ’์„ ํ• ๋‹นํ•œ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์‹œํ€€์Šค ๊ฐ’์„ ์„ ์ ํ•˜๋ฏ€๋กœ ์—ฌ๋Ÿฌ JVM์ด ๋™์‹œ์— ๋™์ž‘ํ•ด๋„ ๊ธฐ๋ณธ ํ‚ค ๊ฐ’์ด ์ถฉ๋Œํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฐ˜๋ฉด์— ์‹œํ€€์Šค ๊ฐ’์„ ํ•œ ๋ฒˆ์— ๋งŽ์ด ์ฆ๊ฐ€์‹œํ‚จ๋‹ค. ์ด๋Ÿฐ ์ƒํ™ฉ์ด ๋ถ€๋‹ด์Šค๋Ÿฝ๊ณ  INSERT ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•˜์ง€ ์•Š์œผ๋ฉด allocationsSize๊ฐ’์„ 1๋กœ ์„ค์ •ํ•œ๋‹ค.


4.6.4 TABLE ์ „๋žต

TABLE ์ „๋žต์€ ํ‚ค ์ƒ์„ฑ ์ „์šฉ ํ…Œ์ด๋ธ”์„ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ  ์—ฌ๊ธฐ์— ์ด๋ฆ„๊ณผ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•  ์ปฌ๋Ÿผ์„ ๋งŒ๋“ค์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค๋ฅผ ํ‰๋‚ด๋‚ด๋Š” ์ „๋žต์ด๋‹ค.

-- ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ‚ค ์ƒ์„ฑ ์šฉ๋„ ํ…Œ์ด๋ธ” ์ƒ์„ฑ
create table MY_SEQUENCES (
    sequence_name varchar(255) not null,
    next_val bigint,
    primary key (sequence_name)
)
@Entity
@TableGenerator(
    name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = "BOARD_SEQ",
    allocationsSize = 1
)
public class BOARD {

    @Id
    @GeneratedValue(
        strategy = GenerationType.TABLE,
        generator = "BOARD_SEQ_GENERATOR"
    )
    private Long id;
}


@TableGenerator

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
name ์‹๋ณ„์ž ์ƒ์„ฑ๊ธฐ ์ด๋ฆ„ ํ•„์ˆ˜
table ํ‚ค์ƒ์„ฑ ํ…Œ์ด๋ธ”๋ช… hibernate_sequences
pkColumnName ์‹œํ€€์Šค ์ปฌ๋Ÿผ๋ช… sequence_name
valueColumnName ์‹œํ€€์Šค ๊ฐ’ ์ปฌ๋Ÿผ๋ช… next_val
pkColumnValue ํ‚ค๋กœ ์‚ฌ์šฉํ•  ๊ฐ’ ์ด๋ฆ„ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„
initialValue ์ดˆ๊ธฐ ๊ฐ’, ๋งˆ์ง€๋ง‰์œผ๋กœ ์ƒ์„ฑ๋œ ๊ฐ’์ด ๊ธฐ์ค€์ด๋‹ค. 0
allocationsSize ์‹œํ€€์Šค ํ•œ ๋ฒˆ ํ˜ธ์ถœ์— ์ฆ๊ฐ€ํ•˜๋Š” ์ˆ˜ (์„ฑ๋Šฅ ์ตœ์ ํ™”์— ์‚ฌ์šฉ๋จ) 50
catalog, schema ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค catalog, schema ์ด๋ฆ„ ย 
uniqueConstraints(DDL) ์œ ๋‹ˆํฌ ์ œ์•ฝ ์กฐ๊ฑด์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ย 


TABLE ์ „๋žต๊ณผ ์ตœ์ ํ™”
TABLE ์ „๋žต์€ ๊ฐ’์„ ์กฐํšŒํ•˜๋ฉด์„œ SELECT ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๋‹ค์Œ ๊ฐ’์œผ๋กœ ์ฆ๊ฐ€์‹œํ‚ค๊ธฐ ์œ„ํ•ด UPDATE ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. SEQUENCE ์ „๋žต๊ณผ ๋น„๊ตํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํ•œ ๋ฒˆ ๋” ํ†ต์‹ ํ•˜๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.


4.6.5 AUTO ์ „๋žต

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐฉ์–ธ์— ๋”ฐ๋ผ IDENTITY, SEQUENCE, TABLE ์ „๋žต ์ค‘ ํ•˜๋‚˜๋ฅผ ์ž๋™์œผ๋กœ ์„ ํƒํ•œ๋‹ค. @GeneratedValue.strategy์˜ ๊ธฐ๋ณธ๊ฐ’์€ AUTO๋‹ค.

@Id @GeneratedValue
private Long id;


4.6.6 ๊ธฐ๋ณธ ํ‚ค ๋งคํ•‘ ์ •๋ฆฌ

  • ์ง์ ‘ ํ• ๋‹น: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ง์ ‘ ์‹๋ณ„์ž ๊ฐ’์„ ํ• ๋‹น
  • SEQUENCE: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค์—์„œ ์‹๋ณ„์ž ๊ฐ’์„ ํš๋“ ํ›„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ
  • TABLE: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค ์ƒ์„ฑ์šฉ ํ…Œ์ด๋ธ”์—์„œ ์‹๋ณ„์ž ๊ฐ’์„ ํš๋“ฑ ํ›„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ
  • IDENTITY: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•ด์„œ ์‹๋ณ„์ž ๊ฐ’์„ ํš๋“ํ•œ ํ›„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ

์‹๋ณ„์ž ์„ ํƒ ์ „๋žต
ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ์„ ํƒํ•˜๋Š” ์ „๋žต์€ ํฌ๊ฒŒ 2๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. ์ž์—ฐ ํ‚ค(natural key)
    • ๋น„์ฆˆ๋‹ˆ์Šค์— ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ํ‚ค
    • ์˜ˆ) ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ, ์ด๋ฉ”์ผ, ์ „ํ™”๋ฒˆํ˜ธ
  2. ๋Œ€๋ฆฌ ํ‚ค(surrogate key)
    • ๋น„์ฆˆ๋‹ˆ์Šค์™€ ๊ด€๋ จ์ด ์—†๋Š” ์ž„์˜๋กœ ๋งŒ๋“ค์–ด์ง„ ํ‚ค, ๋Œ€์ฒด ํ‚ค๋กœ๋„ ๋ถˆ๋ฆฐ๋‹ค
    • ์˜ˆ) ์˜ค๋ผํด ์‹œํ€€์Šค, auto_increment, ํ‚ค ์ƒ์„ฑ ํ…Œ์ด๋ธ” ์‚ฌ์šฉ

๊ถŒ์žฅํ•˜๋Š” ์‹๋ณ„์ž ์„ ํƒ ์ „๋žต
์ž์—ฐ ํ‚ค๋ณด๋‹ค๋Š” ๋Œ€๋ฆฌ ํ‚ค๋ฅผ ๊ถŒ์žฅํ•œ๋‹ค.

  • ์ž์—ฐ ํ‚ค๋Š” ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ํ™˜๊ฒฝ์€ ์–ธ์  ๊ฐ€ ๋ณ€ํ•œ๋‹ค.
  • ํ…Œ์ด๋ธ”์€ ํ•œ ๋ฒˆ ์ •์˜ํ•˜๋ฉด ๋ณ€๊ฒฝํ•˜๊ธฐ ์–ด๋ ต๋‹ค.




4.7 ํ•„๋“œ์™€ ์ปฌ๋Ÿผ ๋งคํ•‘: ๋ ˆํผ๋Ÿฐ์Šค

๋ถ„๋ฅ˜ ๋งคํ•‘ ์–ด๋…ธํ…Œ์ด์…˜ ์„ค๋ช…
ํ•„๋“œ์™€ ์ปฌ๋Ÿผ ๋งคํ•‘ @Column ์ปฌ๋Ÿผ์„ ๋งคํ•‘ํ•œ๋‹ค.
@Enumerated ์ž๋ฐ”์˜ enum ํƒ€์ž…์„ ๋งคํ•‘ํ•œ๋‹ค.
@Temporal ๋‚ ์งœ ํƒ€์ž…์„ ๋งคํ•‘ํ•œ๋‹ค.
@Lob BLOB, CLOB ํƒ€์ž…์„ ๋งคํ•‘ํ•œ๋‹ค.
@Transient ํŠน์ • ํ•„๋“œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋งคํ•‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
๊ธฐํƒ€ @Access JPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ์‹์„ ์ง€์ •ํ•œ๋‹ค.



4.7.1 @Column

@Column์€ ๊ฐ์ฒด ํ•„๋“œ๋ฅผ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ์— ๋งคํ•‘ํ•œ๋‹ค.

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
name ํ•„๋“œ์™€ ๋งคํ•‘ํ•  ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ์ด๋ฆ„ ๊ฐ์ฒด์˜ ํ•„๋“œ ์ด๋ฆ„
insertable (๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ) ์—”ํ‹ฐํ‹ฐ ์ €์žฅ ์‹œ ์ด ํ•„๋“œ๋„ ๊ฐ™์ด ์ €์žฅํ•œ๋‹ค. false๋กœ ์„ค์ •ํ•˜๋ฉด ์ด ํ•„๋“œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. true
updatable (๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ) ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ • ์‹œ ์ด ํ•„๋“œ๋„ ๊ฐ™์ด ์ˆ˜์ •ํ•œ๋‹ค. false๋กœ ์„ค์ •ํ•˜๋ฉด ์ด ํ•„๋“œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. true
table (๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ) ํ•˜๋‚˜์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‘ ๊ฐœ ์ด์ƒ์˜ ํ…Œ์ด๋ธ”์— ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์ง€์ •ํ•œ ํ•„๋“œ๋ฅผ ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์— ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ ํด๋ž˜์Šค๊ฐ€ ๋งคํ•‘๋œ ํ…Œ์ด๋ธ”
nullable (DDL) null ๊ฐ’์˜ ํ—ˆ์šฉ ์—ฌ๋ถ€ ์„ค์ •. true
unique (DDL) @Table์˜ uniqueConstraints์™€ ๊ฐ™์ง€๋งŒ ํ•œ ์ปฌ๋Ÿผ์— ๊ฐ„๋‹จํžˆ ์œ ๋‹ˆํฌ ์ œ์•ฝ์กฐ๊ฑด์„ ๊ฑธ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ๋งŒ์•ฝ ๋‘ ์ปฌ๋Ÿผ ์ด์ƒ์„ ์‚ฌ์šฉํ•ด์„œ ์œ ๋‹ˆํฌ ์ œ์•ฝ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @Table.uniqueConstraints๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ย 
length (DDL) ๋ฌธ์ž ๊ธธ์ด ์ œ์•ฝ์กฐ๊ฑด, String ํƒ€์ž…์—๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. 255
precision, scale (DDL) BigDecimal ํƒ€์ž…์—์„œ ์‚ฌ์šฉํ•œ๋‹ค. precision์€ ์†Œ์ˆ˜์ ์„ ํฌํ•จํ•œ ์ „์ฒด ์ž๋ฆฟ์ˆ˜, scale์€ ์†Œ์ˆ˜์˜ ์ž๋ฆฟ์ˆ˜๋‹ค. double, floatํƒ€์ž…์—๋Š” ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค. precision = 19, scale=2
columnDefinition ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปฌ๋Ÿผ ์ •๋ณด๋ฅผ ์ง์ ‘ ์ค„ ์ˆ˜ ์žˆ๋‹ค. ํ•„๋“œ์˜ ์ž๋ฐ” ํƒ€์ž…๊ณผ ๋ฐฉ์–ธ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ ์ ˆํ•œ ์ปฌ๋Ÿผ ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค.



4.7.2 @Enumerated

์ž๋ฐ”์˜ enum ํƒ€์ž…์„ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
value EnumType.ORDINAL: enum ์ˆœ์„œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ
EnumType.STRING: enum ์ด๋ฆ„์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ
EnumType.ORDINAL



4.7.3 @Temporal

๋‚ ์งœ ํƒ€์ž…(java.util.Date, java.util.Calendar)์„ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
value - TemporalType.DATE: ๋‚ ์งœ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค date ํƒ€์ž…๊ณผ ๋งคํ•‘ (์˜ˆ: 2013-10-11)
- TemporalType.TIME: ์‹œ๊ฐ„, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค time ํƒ€์ž…๊ณผ ๋งคํ•‘ (์˜ˆ: 11:11:11)
- TemporalType.TIMESTAMP: ๋‚ ์งœ์™€ ์‹œ๊ฐ„, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค timestamp ํƒ€์ž…๊ณผ ๋งคํ•‘ (์˜ˆ: 2013-10-11 11:11:11)
TemporalType์€ ํ•„์ˆ˜ ์ง€์ •


4.7.4 @Lob

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค BLOB, CLOB ํƒ€์ž…๊ณผ ๋งคํ•‘ํ•œ๋‹ค. ๋งคํ•‘ํ•˜๋Š” ํ•„๋“œ ํƒ€์ž…์— ๋”ฐ๋ผ ๋งคํ•‘์„ ๋‹ฌ๋ฆฌํ•œ๋‹ค.

  • CLOB: String, char[], java.sql.CLOB
  • BLOB: byte[], java.sql.BLOB


4.7.5 @Transient

์ด ํ•„๋“œ๋Š” ๋งคํ•‘ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ์กฐํšŒํ•˜์ง€ ์•Š๋Š”๋‹ค.


4.7.6 @Access

JPA๊ฐ€ ์—”ํ‹ฐํ‹ฐ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ์‹์„ ์ง€์ •ํ•œ๋‹ค.

  • AccessType.FIELD: ํ•„๋“œ ์ ‘๊ทผ
  • AccessType.PROPERTY: ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ, ์ ‘๊ทผ์ž(Getter) ์‚ฌ์šฉ
@Entity
@Access(AccessType.FIELD)
public class Member {...}
@Entity
@Access(AccessType.PROPERTY)
public class Member {...}
/**
 * ๋‘ ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
 *
 * @Id๋Š” ํ•„๋“œ ์ ‘๊ทผ ๋ฐฉ์‹
 * getFullName()๋Š” ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ ๋ฐฉ์‹
 * Member ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•˜๋ฉด ํ…Œ์ด๋ธ”์˜ FULLNAME ์ปฌ๋Ÿผ์—
 * firstName + lastName์˜ ๊ฒฐ๊ณผ๊ฐ€ ์ €์žฅ๋œ๋‹ค.
 */
@Entity
public class Member {
    
    @Id
    private String id;

    @Transient
    private String firstName;

    @Transient
    private String lastName;

    @Access(AccessType.PROPERTY)
    public String getFullName() {
        return firstName + lastName;
    }
    ...
}






5. ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ ๊ธฐ์ดˆ

์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ ํ‚ค์›Œ๋“œ ์ •๋ฆฌ

  • ๋ฐฉํ–ฅ(Direction): [๋‹จ๋ฐฉํ–ฅ, ์–‘๋ฐฉํ–ฅ]์ด ์žˆ๋‹ค. ๋ฐฉํ–ฅ์€ ๊ฐ์ฒด๊ด€๊ณ„์—์„œ๋งŒ ์กด์žฌํ•˜๊ณ  ํ…Œ์ด๋ธ” ๊ด€๊ณ„๋Š” ํ•ญ์ƒ ์–‘๋ฐฉํ–ฅ์ด๋‹ค.
  • ๋‹ค์ค‘์„ฑ(Multiplcity): [๋‹ค๋Œ€์ผ(N:1), ์ผ๋Œ€๋‹ค(1:N), ์ผ๋Œ€์ผ(1:1), ๋‹ค๋Œ€๋‹ค(N:M)] ๋‹ค์ค‘์„ฑ์ด ์žˆ๋‹ค.
  • ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ(Owner): ๊ฐ์ฒด๋ฅผ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋กœ ๋งŒ๋“ค๋ฉด ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์„ ์ •ํ•ด์•ผ ํ•œ๋‹ค.


5.1 ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„


๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„์™€ ํ…Œ์ด๋ธ” ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฐจ์ด

  • ๊ฐ์ฒด
    • ์ฐธ์กฐ(์ฃผ์†Œ)๋กœ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ๋Š”๋‹ค.
    • ์–ธ์ œ๋‚˜ ๋‹จ๋ฐฉํ–ฅ
    • ์–‘๋ฐฉํ–ฅ์„ ํ•˜๋ ค๋ฉด ์„œ๋กœ๋‹ค๋ฅธ ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„ 2๊ฐœ๋กœ ๊ตฌํ˜„
  • ํ…Œ์ด๋ธ”
    • ์™ธ๋ž˜ ํ‚ค๋กœ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ๋Š”๋‹ค.
    • ์™ธ๋ž˜ ํ‚ค ํ•˜๋‚˜๋กœ ์–‘๋ฐฉํ–ฅ ์กฐ์ธ ๊ฐ€๋Šฅ


5.1.1 ์ˆœ์ˆ˜ํ•œ ๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„

๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๊ฒƒ์„ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰์ด๋ผ ํ•œ๋‹ค.

public static void main(String[] args) {
    Member member1 = new Memeber("member1", "ํšŒ์›1");
    Member member2 = new Memeber("member2", "ํšŒ์›1");
    Team team = new Team("team", "ํŒ€");

    member1.setTeam(team);
    member2.setTeam(team);

    // ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰
    Team findTeam = member1.getTeam();
}



5.1.2 ํ…Œ์ด๋ธ” ์—ฐ๊ด€๊ด€๊ณ„

์™ธ๋ž˜ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์ž‡๋Š”๋ฐ ์ด๊ฒƒ์„ ์กฐ์ธ์ด๋ผ ํ•œ๋‹ค.

SELECT T.*
FROM MEMBER M
    JOIN TEAM T ON M.TEAM_ID = T.ID
WHERE M.MEMBER_ID = 'member1'



5.1.3 ๊ฐ์ฒด ๊ด€๊ณ„ ๋งคํ•‘

@Entity
public class Member {

    @Id
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

    //์—ฐ๊ด€ ๊ด€๊ณ„ ๋งคํ•‘
    @ManyToOne
    @JoinColumn(name="TEAM_ID")
    private Team team;

    //์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •
    public void setTeam(Team team) {
        this.team = team;
    }
    ...์ƒ๋žต...
}
@Entity
public class Team {
    
    @Id
    @Column(name = "TEAM_ID")
    private String id;
    ...์ƒ๋žต...
}


@ManyToOne

  • ๋‹ค๋Œ€์ผ(N:1) ๊ด€๊ณ„๋ผ๋Š” ๋งคํ•‘ ์ •๋ณด
  • ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ํ•  ๋•Œ ๋‹ค์ค‘์„ฑ์„ ๋‚˜ํƒ€๋‚ด๋Š” ์–ด๋…ธํ…Œ์ด์…˜ ํ•„์ˆ˜๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

@JoinColumn(name=โ€TEAM_IDโ€)

  • ์กฐ์ธ์ปฌ๋Ÿผ์€ ์™ธ๋ž˜ ํ‚ค๋ฅผ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • name ์†์„ฑ์— ๋งคํ•‘ํ•  ์™ธ๋ž˜ ํ‚ค ์ด๋ฆ„์„ ์ง€์ •ํ•œ๋‹ค. (์—ฌ๊ธฐ์„  ํŒ€ ํ…Œ์ด๋ธ”์˜ TEAM_ID)
  • ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.


5.1.4 @JoinColumn

@JoinColumn์€ ์™ธ๋ž˜ ํ‚ค๋ฅผ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
name ๋งคํ•‘ํ•  ์™ธ๋ž˜ ํ‚ค ์ด๋ฆ„ ํ•„๋“œ๋ช… + _ + ์ฐธ์กฐํ•˜๋Š” ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿฌ๋ช…
referencedColumnName ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ๋Œ€์ƒ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ๋ช… ์ฐธ์กฐํ•˜๋Š” ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค ์ปฌ๋Ÿผ๋ช…
foreignKey (DDL) ์™ธ๋ž˜ ํ‚ค ์ œ์•ฝ์กฐ๊ฑด์„ ์ง์ ‘ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์†์„ฑ์€ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•  ๋•Œ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. ย 
unique, nullable, insertable, updatable, columnDefinition, table @Column์˜ ์†์„ฑ๊ณผ ๊ฐ™๋‹ค. ย 


5.1.5 @ManyToOne

@ManyToOne ์–ด๋…ธํ…Œ์ด์…˜์€ ๋‹ค๋Œ€์ผ ๊ด€๊ณ„์—์„œ ์‚ฌ์šฉํ•œ๋‹ค.

์†์„ฑ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ๊ฐ’
optional false๋กœ ์„ค์ •ํ•˜๋ฉด ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ํ•ญ์ƒ ์žˆ์–ด์•ผ ํ•œ๋‹ค. true
fetch ๊ธ€๋กœ๋ฒŒ ํŽ˜์น˜ ์ „๋žต์„ ์„ค์ •ํ•œ๋‹ค. @ManyToOne=FetchType.EAGER @OneToMany=FetchType.LAZY
cascade ์˜์†์„ฑ ์ „์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค. ย 
targetEntity (๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ) ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ์˜ ํƒ€์ž… ์ •๋ณด๋ฅผ ์„ค์ •ํ•œ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด๋„ ์ œ๋„ค๋ฆญ์œผ๋กœ ํƒ€์ž… ์ •๋ณด๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ย 



5.2 ์—ฐ๊ด€๊ด€๊ณ„ ์‚ฌ์šฉ

์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋“ฑ๋ก, ์ˆ˜์ •, ์‚ญ์ œ, ์กฐํšŒํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณธ๋‹ค.


5.2.1 ์ €์žฅ

JPA์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ• ๋•Œ ์—ฐ๊ด€๋œ ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋Š” ์˜์† ์ƒํƒœ์—ฌ์•ผ ํ•œ๋‹ค.

public void testSave() {

    // ํŒ€1 ์ €์žฅ
    Team team1 = new Team("team1", "ํŒ€1");
    em.persist(team1);

    // ํšŒ์›1 ์ €์žฅ
    Member member1 = new Member("member1", "ํšŒ์›1");
    member1.setTeam(team1); //์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ • member1 -> team1
    em.persist(member1);

    // ํšŒ์›2 ์ €์žฅ
    Member member2 = new Member("member2", "ํšŒ์›2");
    member2.setTeam(team1); //์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ • member2 -> team1
    em.persist(member2);
}


5.2.2 ์กฐํšŒ

์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ 2๊ฐ€์ง€๋‹ค.

  • ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰(๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•œ ์กฐํšŒ)
  • ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์‚ฌ์šฉ(JPQL)

๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰

Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰
System.out.println("ํŒ€ ์ด๋ฆ„ = " + team.getName());


๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ ์‚ฌ์šฉ

public static void testJPQL(EntityManager em) {
    String jpql1 = "select m from Member m join m.team t where t.name=:teamName";

    List<Member> resultList = em.createQuery(jpql1, Member.class)
        .setParameter("teamName", "ํŒ€1")
        .getResultList();

    for (Member member : resultList) {
        System.out.println("[query] member.username = " + member.getUsername());
    }
}

/*
* ๊ฒฐ๊ณผ
* [query] member.username=ํšŒ์›1
* [query] member.username=ํšŒ์›2
*/

์‹คํ–‰๋˜๋Š” SQL์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

SELECT M.* FROM MEMBER MEMBER 
INNER JOIN 
    TEAM TEAM ON MEMBER.TEAM_ID = TEAM1_.ID 
WHERE
    TEAM1_.NAME='ํŒ€1'

์‹คํ–‰๋œ SQL๊ณผ JPQL์„ ๋น„๊ตํ•˜๋ฉด JPQL์€ ๊ฐ์ฒด(์—”ํ‹ฐํ‹ฐ)๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๊ณ  SQL๋ณด๋‹ค ๊ฐ„๊ฒฐํ•˜๋‹ค.


5.2.3 ์ˆ˜์ •

private static void updateRelation(EntityManager em) {

    // ์ƒˆ๋กœ์šด ํŒ€2
    Team team2 = new Team("team2", "ํŒ€2");
    em.persist(team2);

    //ํšŒ์›1์— ์ƒˆ๋กœ์šด ํŒ€2 ์„ค์ •
    Member member = em.find(Member.class, "member1");
    member.setTeam(team2);
}

์‹คํ–‰๋˜๋Š” ์ˆ˜์ • SQL์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

UPDATE MEMBER
SET
    TEAM_ID='team2', ...
WHERE ID = 'member1' 



5.2.4 ์—ฐ๊ด€๊ด€๊ณ„ ์ œ๊ฑฐ

private static void deleteRelation(EntityManager em) {

    Member member1 = em.find(Member.class, "member1");
    member1.setTeam(null); //์—ฐ๊ด€๊ด€๊ณ„ ์ œ๊ฑฐ
}

์‹คํ–‰๋˜๋Š” ์—ฐ๊ด€๊ด€๊ณ„ ์ œ๊ฑฐ SQL์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

UPDATE MEMBER
SET
    TEAM_ID=null, ...
WHERE ID = 'member1' 



5.2.5 ์—ฐ๊ด€๊ด€๊ณ„ ์‚ญ์ œ

์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ญ์ œํ•˜๋ ค๋ฉด ๊ธฐ์กด์— ์žˆ๋˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋จผ์ € ์ œ๊ฑฐํ•˜๊ณ  ์‚ญ์ œํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์™ธ๋ž˜ ํ‚ค ์ œ์•ฝ์กฐ๊ฑด์œผ๋กœ ์ธํ•ด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

member1.setTeam(null); // ํšŒ์›1 ์—ฐ๊ด€๊ด€๊ณ„ ์ œ๊ฑฐ
member2.setTeam(null); // ํšŒ์›2 ์—ฐ๊ด€๊ด€๊ณ„ ์ œ๊ฑฐ
em.remove(team);       // ํŒ€ ์‚ญ์ œ




5.3 ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„

๊ฐ์ฒด

  • ํšŒ์›๊ณผ ํŒ€์€ ๋‹ค๋Œ€์ผ ๊ด€๊ณ„
  • ํŒ€์—์„œ ํšŒ์›์˜ ๊ด€๊ณ„๋Š” ์ผ๋Œ€๋‹ค ๊ด€๊ณ„

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์€ ์™ธ๋ž˜ ํ‚ค ํ•˜๋‚˜๋กœ ์–‘๋ฐฉํ–ฅ ์กฐํšŒ


5.3.1 ์–‘๋ฐฉํ•ญ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘

@Entity
public class Member {
    ...์ƒ๋žต...

    // ์—ฐ๊ด€ ๊ด€๊ณ„ ๋งคํ•‘
    @ManyToOne
    @JoinColumn(name="TEAM_ID")
    private Team team;

    ...์ƒ๋žต...
}
@Entity
public class Team {
    ...์ƒ๋žต...

    // @OneToMany ๋งคํ•‘ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.
    // mappedBy ์†์„ฑ์€ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์ผ ๋•Œ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ๋ฐ˜๋Œ€์ชฝ ๋งคํ•‘์˜ ํ•„๋“œ ์ด๋ฆ„์„ ๊ฐ’์œผ๋กœ ์ฃผ๋ฉด ๋œ๋‹ค.
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<Member>();
    
    ...์ƒ๋žต...  
}




5.4 ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ

  • ๊ฐ์ฒด์—๋Š” ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ผ๋Š” ๊ฒƒ์ด ์—†๋‹ค.
  • ์„œ๋กœ ๋‹ค๋ฅธ ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ 2๊ฐœ๋ฅผ ์–‘๋ฐฉํ–ฅ์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•  ๋ฟ์ด๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋กœ ์„ค์ •ํ•˜๋ฉด ๊ฐ์ฒด์˜ ์ฐธ์กฐ๋Š” ๋‘˜์ธ๋ฐ ์™œ๋ž˜ ํ‚ค๋Š” ํ•˜๋‚˜๋‹ค.
  • JPA์—์„œ๋Š” ๋‘ ๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ •ํ•ด์„œ ํ…Œ์ด๋ธ”์˜ ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋Š”๋ฐ ์ด๊ฒƒ์„ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด๋ผ ํ•œ๋‹ค.


5.4.1 ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์˜ ๊ทœ์น™: ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ

  • ๋‘ ์—ฐ๊ด€๊ด€๊ณ„ ์ค‘ ํ•˜๋‚˜๋ฅผ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์œผ๋กœ ์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ๋งŒ์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ด€๊ด€๊ณ„์™€ ๋งคํ•‘๋˜๊ณ  ์™œ๋ž˜ ํ‚ค๋ฅผ ๊ด€๋ฆฌ(๋“ฑ๋ก,์ˆ˜์ •,์‚ญ์ œ)ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์€ ์ฝ๊ธฐ๋งŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

mappedBy

  • ์ฃผ์ธ์€ mappedBy ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ฃผ์ธ์ด ์•„๋‹ˆ๋ฉด mappedBy ์†์„ฑ์„ ์‚ฌ์šฉํ•ด์„œ ์†์„ฑ์˜ ๊ฐ’์œผ๋กœ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์„ ์ง€์ •ํ•œ๋‹ค. (โ€˜๋‚ด๊ฐ€ ์ƒ๋Œ€์—๊ฒŒ ๋งคํ•‘๋˜์—ˆ๋‹คโ€™๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํŽธํ•จ)
    ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์„ ์ •ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์™ธ๋ž˜ ํ‚ค ๊ด€๋ฆฌ์ž๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค.


5.4.2 ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์žˆ๋Š” ๊ณณ

  • ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์žˆ๋Š” ๊ณณ์œผ๋กœ ์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋‹ค๋Œ€์ผ, ์ผ๋Œ€๋‹ค ๊ด€๊ณ„์—์„œ๋Š” ํ•ญ์ƒ ๋‹ค ์ชฝ์ด ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ฐ€์ง„๋‹ค.
  • ๋‹ค ์ชฝ์ธ @ManyToOne์€ ํ•ญ์ƒ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด ๋˜๋ฏ€๋กœ mappedBy๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์—†๋‹ค.
  • ๋”ฐ๋ผ์„œ @ManyToOne์—๋Š” mappedBy ์†์„ฑ์ด ์—†๋‹ค.



5.5 ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ์ €์žฅ

public void testSave() {

    // ํŒ€1 ์ €์žฅ
    Team team1 = new Team("team1", "ํŒ€1");
    em.persist(team1);

    // ํšŒ์›1 ์ €์žฅ
    Member member1 = new Member("member1", "ํšŒ์›1");
    member1.setTeam(team1); //์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ • member1 -> team1
    em.persist(member1);

    // ํšŒ์›2 ์ €์žฅ
    Member member2 = new Member("member2", "ํšŒ์›2");
    member2.setTeam(team1); //์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ • member2 -> team1
    em.persist(member2);
}
// ์ฃผ์ธ์ด ์•„๋‹Œ ๊ณณ์— ์ž…๋ ฅ๋œ ๊ฐ’์€ ์™ธ๋ž˜ ํ‚ค์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค. 
team1.getMembers().add(member1); //๋ฌด์‹œ(์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด ์•„๋‹˜)
team1.getMembers().add(member2); //๋ฌด์‹œ(์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด ์•„๋‹˜)
// Member.team์€ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด๋‹ค.
// ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ์ด๊ณณ์— ์ž…๋ ฅ๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•ด ์™ธ๋ž˜ ํ‚ค ๊ด€๋ฆฌํ•œ๋‹ค.
member1.setTeam(team1); //์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •(์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ)
member2.setTeam(team1); //์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •(์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ)




5.6 ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์˜์ 

๊ฐ€์žฅ ํ”ํ•œ ์‹ค์ˆ˜๋Š” ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์—๋Š” ๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๊ณ , ์ฃผ์ธ์ด ์•„๋‹Œ ๊ณณ์—๋งŒ ๊ฐ’์„ ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

public void testSaveNonOwner() {

    // ํšŒ์›1 ์ €์žฅ
    Member member1 = new Member("member1", "ํšŒ์›1");
    em.persist(member1);

    // ํšŒ์›2 ์ €์žฅ
    Member member2 = new Member("member2", "ํšŒ์›2");
    em.persist(member2);

    Team team1 = new Team("team1", "ํŒ€1");

    // ์ฃผ์ธ์ด ์•„๋‹Œ ๊ณณ์— ์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •
    team1.getMembers().add(member1);
    team2.getMembers().add(member2);

    em.persist(team1);
}

์กฐํšŒ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

MEMBER_ID USERNAME TEAM_ID
member1 ํšŒ์›1 null
member2 ํšŒ์›2 null



5.6.1 ์ˆœ์ˆ˜ํ•œ ๊ฐ์ฒด๊นŒ์ง€ ๊ณ ๋ คํ•œ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„

๊ทธ๋ ‡๋‹ค๋ฉด ์ •๋ง ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์—๋งŒ ๊ฐ’์„ ์ €์žฅํ•˜๊ณ  ์ฃผ์ธ์ด ์•„๋‹Œ ๊ณณ์—๋Š” ๊ฐ’์„ ์ €์žฅํ•˜์ง€ ์•Š์•„๋„ ๋ ๊นŒ? ๊ฐ์ฒด ๊ด€์ ์—์„œ๋Š” ์–‘์ชฝ ๋ฐฉํ–ฅ ๋ชจ๋‘ ๊ฐ’์„ ์ž…๋ ฅํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์•ˆ์ „ํ•˜๋‹ค.

member1.setTeam(team1); // ํšŒ์› -> ํŒ€
team1.getMembers().add(member1); // ํŒ€ -> ํšŒ์›



5.6.2 ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์†Œ๋“œ

ํ•œ ๋ฒˆ์— ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์†Œ๋“œ๋ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‹ค์ˆ˜๋„ ์ค„์–ด๋“ค๊ณ  ์ข€ ๋” ํŽธํ•˜๊ฒŒ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

public class Member {

    private Team team;

    public void setTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);
    }
}



5.6.3 ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์†Œ๋“œ ์ž‘์„ฑ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ๋Š” ๊ธฐ์กด ํŒ€์ด ์žˆ์œผ๋ฉด ๊ธฐ์กด ํŒ€๊ณผ ํšŒ์›์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์‚ญ์ œํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

member1.setTeam(teamA); // 1
member1.setTeam(teamB); // 2
Member findMember = teamA.getMember(); // member1์ด ์—ฌ์ „ํžˆ ์กฐํšŒ๋œ๋‹ค.
public void setTeam(Team team) {

    // ๊ธฐ์กด ํŒ€๊ณผ ๊ด€๊ณ„๋ฅผ ์ œ๊ฑฐ
    if (this.team != null) {
        this.team.getMembers().remove(this);
    }    
    this.team = team;
    team.getMembers().add(this);
}




5.7 ์ •๋ฆฌ

  • ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์€ ์–ธ์ œ๋‚˜ ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์ด๋‹ค.
  • ์–‘๋ฐฉํ–ฅ์€ ๋‹จ๋ฐฉํ–ฅ์— ์ฃผ์ธ์ด ์•„๋‹Œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ–ˆ์„ ๋ฟ์ด๋‹ค.
  • ์–‘๋ฐฉํ–ฅ์˜ ์žฅ์ ์€ ๋ฐ˜๋Œ€๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋œ ๊ฒƒ ๋ฟ์ด๋‹ค.
  • ์ฃผ์ธ์˜ ๋ฐ˜๋Œ€ํŽธ์€ mappedBy๋กœ ์ฃผ์ธ์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • ์ฃผ์ธ์˜ ๋ฐ˜๋Œ€ํŽธ์€ ๋‹จ์ˆœํžˆ ๋ณด์—ฌ์ฃผ๋Š” ์ผ๋งŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์€ ์™ธ๋ž˜ ํ‚ค์˜ ์œ„์น˜์™€ ๊ด€๋ จํ•ด์„œ ์ •ํ•ด์•ผ์ง€ ๋น„์ฆˆ๋‹ˆ์Šค ์ค‘์š”๋„๋กœ ์ ‘๊ทผํ•˜๋ฉด ์•ˆ๋œ๋‹ค.





6. ๋‹ค์–‘ํ•œ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘

์—”ํ‹ฐํ‹ฐ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•  ๋•Œ ๊ณ ๋ ค์‚ฌํ•ญ

  • ๋‹ค์ค‘์„ฑ
    • ๋‹ค๋Œ€์ผ(@ManyToOne)
    • ์ผ๋Œ€๋‹ค(@OneToMany)
    • ์ผ๋Œ€์ผ(@OneToOne)
    • ๋‹ค๋Œ€๋‹ค(@ManyToMany)
  • ๋‹จ๋ฐฉํ–ฅ, ์–‘๋ฐฉํ–ฅ
    • ๋‹จ๋ฐฉํ–ฅ: ํ•œ์ชฝ๋งŒ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ
    • ์–‘๋ฐฉํ–ฅ: ์–‘์ชฝ์ด ์„œ๋กœ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ
  • ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ

6.1 ๋‹ค๋Œ€์ผ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์˜ ์ผ๋Œ€๋‹ค ๊ด€๊ณ„์—์„œ ์™ธ๋ž˜ ํ‚ค๋Š” ํ•ญ์ƒ ๋‹ค์ชฝ์— ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐ์ฒด ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์—์„œ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ ํ•ญ์ƒ ๋‹ค์ชฝ์ด๋‹ค.


6.1.1 ๋‹ค๋Œ€์ผ ๋‹จ๋ฐฉํ–ฅ [N:1]

  • @ManyToOne ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ ๋‹ค๋Œ€์ผ ๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•œ๋‹ค.
  • @JoinColumn์€ ํ•„๋“œ๋ฅผ ์™ธ๋ž˜ ํ‚ค์™€ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
public class Member {
    ...์ƒ๋žต...
    @ManyToOne
    @JoinCoulmn(name="TEAM_ID")
    private Team team;
    ...์ƒ๋žต...
}



6.1.2 ๋‹ค๋Œ€์ผ ์–‘๋ฐฉํ–ฅ [N:1, 1:N]

public class Member {
    ...์ƒ๋žต...
    @ManyToOne
    @JoinCoulmn(name="TEAM_ID")
    private Team team;

    public void setTeam(Team team) {
        this.team = team;

        // ๋ฌดํ•œ๋ฃจํ”„์— ๋น ์ง€์ง€ ์•Š๋„๋ก ์ฒดํฌ
        if(!team.getMembers().contains(this)) {
            team.getMembers().add(this);
        }
    }
    ...์ƒ๋žต...
}
public class Team {
    ...์ƒ๋žต...
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    public void addMember(Member member) {
        this.members.add(member);

        // ๋ฌดํ•œ๋ฃจํ”„์— ๋น ์ง€์ง€ ์•Š๋„๋ก ์ฒดํฌ
        if (member.getTeam() != this) {
            member.setTeam(this);
        }
    }
    ...์ƒ๋žต...
}
  • ์–‘๋ฐฉํ–ฅ์€ ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์žˆ๋Š” ์ชฝ์ด ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์ด๋‹ค.
  • ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋Š” ํ•ญ์ƒ ์„œ๋กœ๋ฅผ ์ฐธ์กฐํ•ด์•ผ ํ•œ๋‹ค.



6.2 ์ผ๋Œ€๋‹ค

์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•˜๋‚˜ ์ด์ƒ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ž๋ฐ” ์ปฌ๋ ‰์…˜์ธ Collection, List, Set, Map ์ค‘์— ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

6.2.1 ์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ [1:N]

public class Team {
    ...์ƒ๋žต...
    @OneToMany
    @JoinColumn(name = "TEAM_ID") // MEMBER ํ…Œ์ด๋ธ”์˜ TEAM_ID (FK)
    private List<Member> members = new ArrayList<>();
    ...์ƒ๋žต...
}
  • ์ผ๋Œ€๋‹ค ๊ด€๊ณ„์—์„œ๋Š” ์ผ์ด ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด๋‹ค.
  • ์ผ ์ชฝ์—์„œ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ๋œ๋‹ค.
  • ์ผ๋Œ€ ๋‹ค ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•  ๋•Œ๋Š” @JoinColumn์„ ๋ช…์‹œํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด JPA๋Š” ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์„ ์ค‘๊ฐ„์— ๋‘๊ณ  ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์กฐ์ธ ํ…Œ์ด๋ธ” ์ „๋žต์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

์ผ๋Œ€๋‹ค ๋ฌธ์ œ์ 

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งคํ•‘ํ•œ ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ ๋‹ค์„ ํ…Œ์ด๋ธ”์˜ ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
  • ์„ฑ๋Šฅ ๋ฌธ์ œ
Member member1 = new Memeber("member1");
Member member2 = new Memeber("member2");

Team team1 = new Team("team1");
team1.getMembers().add(member1);
team1.getMembers().add(member2);

em.persist(member1); // INSERT MEMBER1
em.persist(member2); // INSERT MEMBER2
em.persist(team1); // INSERT TEAM, UPDATE MEMBER1, UPDATE MEMBER2

tx.commit();
-- ์—…๋ฐ์ดํŠธ ์ฟผ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋œ๋‹ค.
insert into Member (MEMBER_ID, username) values (null, ?);
insert into Team (TEAM_ID, name) values (null, ?);
update Member set TEAM_ID=? where MEMBER_ID=?

์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘๋ณด๋‹ค๋Š” ๋‹ค๋Œ€์ผ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์„ ์‚ฌ์šฉํ•˜์ž



6.2.2 ์ผ๋Œ€๋‹ค ์–‘๋ฐฉํ–ฅ [1:N, N:1]

public class Team {
    ...์ƒ๋žต...
    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();
    ...์ƒ๋žต...
}
public class Member {
    ...์ƒ๋žต...
    @ManyToOne
    @JoinCoulmn(name = "TEAM_ID",
        insertable = false, updatable = false) // ์ฝ๊ธฐ ์ „์šฉ ๋งคํ•‘
    private Team team;
    ...์ƒ๋žต...
}

์ผ๋Œ€๋‹ค ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์ด ๊ฐ€์ง€๋Š” ๋‹จ์ ์„ ๊ทธ๋Œ€๋กœ ๊ฐ€์ง„๋‹ค.



6.3 ์ผ๋Œ€์ผ [1:1]

์ผ๋Œ€์ผ ๊ด€๊ณ„ ํŠน์ง•

  • ์ผ๋Œ€์ผ ๊ด€๊ณ„๋Š” ๊ทธ ๋ฐ˜๋Œ€๋„ ์ผ๋Œ€์ผ ๊ด€๊ณ„๋‹ค.
  • ํ…Œ์ด๋ธ” ๊ด€๊ณ„์—์„œ ์ผ๋Œ€๋‹ค, ๋‹ค๋Œ€์ผ์€ ํ•ญ์ƒ ๋‹ค(N)์ชฝ์ด ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ฐ€์ง„๋‹ค. ๋ฐ˜๋ฉด ์ผ๋Œ€์ผ ๊ด€๊ณ„๋Š” ์ฃผ ํ…Œ์ด๋ธ”์ด๋‚˜ ๋Œ€์ƒ ํ…Œ์ด๋ธ” ๋‘˜ ์ค‘ ์–ด๋Š ๊ณณ์ด๋‚˜ ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.


6.3.1 ์ฃผ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค

์ฃผ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์žˆ๋Š” ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณด๊ณ , ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋„ ์‚ดํŽด ๋ณด์ž.

๋‹จ๋ฐฉํ–ฅ

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    // @OneToOne์„ ์‚ฌ์šฉํ•ด ์ผ๋Œ€์ผ ๋งคํ•‘ํ•œ๋‹ค.
    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
    ...์ƒ๋žต...
}
@Entity
public class Locker {
    @Id @GeneratedValue
    @Column(name = "LOCKER_ID")
    private Long id;

    ...์ƒ๋žต...
}


์–‘๋ฐฉํ–ฅ

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
    ...์ƒ๋žต...
}
@Entity
public class Locker {
    @Id @GeneratedValue
    @Column(name = "LOCKER_ID")
    private Long id;

    @OneToOne(mappedBy = "locker") // ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ ํ•„๋“œ๋ช…์„ ์„ ์–ธ
    private Member member;

    ...์ƒ๋žต...
}



6.3.2 ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค

๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์žˆ๋Š” ์ผ๋Œ€์ผ ๊ด€๊ณ„๋ฅผ ์•Œ์•„๋ณธ๋‹ค.

๋‹จ๋ฐฉํ–ฅ

์ผ๋Œ€์ผ ๋‹จ๋ฐฉํ–ฅ์€ ์ด๋Ÿฐ ๋งคํ•‘์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

์–‘๋ฐฉํ–ฅ

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @OneToOne(mappedBy = "member")
    private Locker locker;
    ...์ƒ๋žต...
}
@Entity
public class Locker {
    @Id @GeneratedValue
    @Column(name = "LOCKER_ID")
    private Long id;

    @OneToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    ...์ƒ๋žต...
}

์ผ๋Œ€์ผ ๋งคํ•‘์—์„œ ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค๋ฅผ ๋‘๊ณ  ์‹ถ์œผ๋ฉด ์ด๋ ‡๊ฒŒ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ๋งคํ•‘ํ•œ๋‹ค.



6.4 ๋‹ค๋Œ€๋‹ค [N:N]

ํ…Œ์ด๋ธ” 2๊ฐœ๋กœ๋Š” ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋‹ค.

์ค‘๊ฐ„์— ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.


6.4.1 ๋‹ค๋Œ€๋‹ค: ๋‹จ๋ฐฉํ–ฅ

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @ManyToMany
    @JoinTable(
        name = "MEMBER_PRODUCT", // ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ” ์ง€์ •
        joinColumn = @JoinColumn(name = "MEMBER_ID"), // ํ˜„์žฌ ์—”ํ‹ฐํ‹ฐ์™€ ๋งคํ•‘ํ•  ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์˜ ์กฐ์ธ ์ปฌ๋Ÿผ
        inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID") // ์ƒ๋Œ€ ์—”ํ‹ฐํ‹ฐ์™€ ๋งคํ•‘ํ•  ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์˜ ์กฐ์ธ ์ปฌ๋Ÿผ
    )
    private List<Product> products = new ArrayList<Product>();
    ...์ƒ๋žต...
}
@Entity
public class Product {
    @Id @Column(name = "PRODUCT_ID")
    private Long id;
    ...์ƒ๋žต...
}



6.4.2 ๋‹ค๋Œ€๋‹ค: ์–‘๋ฐฉํ–ฅ

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @ManyToMany
    @JoinTable(
        name = "MEMBER_PRODUCT", // ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ” ์ง€์ •
        joinColumn = @JoinColumn(name = "MEMBER_ID"), // ํ˜„์žฌ ์—”ํ‹ฐํ‹ฐ์™€ ๋งคํ•‘ํ•  ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์˜ ์กฐ์ธ ์ปฌ๋Ÿผ
        inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID") // ์ƒ๋Œ€ ์—”ํ‹ฐํ‹ฐ์™€ ๋งคํ•‘ํ•  ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์˜ ์กฐ์ธ ์ปฌ๋Ÿผ
    )
    private List<Product> products = new ArrayList<Product>();
    ...์ƒ๋žต...
}
@Entity
public class Product {
    @Id @Column(name = "PRODUCT_ID")
    private Long id;

    @ManyToMany(mappedBy = "products") // ์—ญ๋ฐฉํ–ฅ ์ถ”๊ฐ€
    private List<Member> members;
    ...์ƒ๋žต...
}



6.4.3 ๋‹ค๋Œ€๋‹ค: ๋งคํ•‘์˜ ํ•œ๊ณ„์™€ ๊ทน๋ณต, ์—ฐ๊ฒฐ ์—”ํ‹ฐํ‹ฐ ์‚ฌ์šฉ

์—ฐ๊ฒฐํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค๋ฅผ @IdClass ์‚ฌ์šฉํ•œ ๋ณตํ•ฉ ํ‚ค ์‚ฌ์šฉ ์˜ˆ์ œ๋ผ์„œ ๋„˜๊น€.


6.4.4 ๋‹ค๋Œ€๋‹ค: ์ƒˆ๋กœ์šด ๊ธฐ๋ณธ ํ‚ค ์‚ฌ์šฉ

  • @ManyToMany๋Š” ํŽธ๋ฆฌํ•˜์ง€๋งŒ, ์‹ค๋ฌด์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ์—๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค.
  • ๋ณดํ†ต์€ ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์— ์ƒˆ๋กœ์šด ์ปฌ๋Ÿผ์ด ์ถ”๊ฐ€๋˜๊ธฐ ๋งˆ๋ จ์ด๋‹ค.
  • ๊ฒฐ๊ตญ ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์„ ๋งคํ•‘ํ•˜๋Š” ์—ฐ๊ฒฐ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค๊ณ  ์ผ๋Œ€๋‹ค, ๋‹ค๋Œ€์ผ ๊ด€๊ณ„๋กœ ํ’€์–ด์•ผ ํ•œ๋‹ค.
@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts;
    ...์ƒ๋žต...
}
@Entity
public class Product {
    @Id @Column(name = "PRODUCT_ID")
    private Long id;

    // ์ƒํ’ˆ ์—”ํ‹ฐํ‹ฐ์—์„œ ํšŒ์›์ƒํ’ˆ ์—”ํ‹ฐํ‹ฐ๋กœ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰ ๊ธฐ๋Šฅ์ด ํ•„์š” ์—†๋‹ค ํŒ๋‹จ.
    // ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•˜๋‹ค.
    // @OneToMany(mappedBy = "product")
    // private List<MemberProduct> memberProducts = new ArrayList<>();
}
@Entity
public class MemberProduct {

    @Id @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;
}



6.4.5 ๋‹ค๋Œ€๋‹ค ์—ฐ๊ด€๊ด€๊ณ„ ์ •๋ฆฌ

  • ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ์ผ๋Œ€๋‹ค, ๋‹ค๋Œ€์ผ ๊ด€๊ณ„๋กœ ํ’€์–ด๋‚ธ๋‹ค.
  • ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค ๋•Œ ์‹๋ณ„์ž๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ• ์ง€ ์„ ํƒํ•œ๋‹ค.
    • ์‹๋ณ„ ๊ด€๊ณ„: ๋ฐ›์•„์˜จ ์‹๋ณ„์ž๋ฅผ ๊ธฐ๋ณธ ํ‚ค + ์™ธ๋ž˜ ํ‚ค๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
    • ๋น„์‹๋ณ„ ๊ด€๊ณ„: ๋ฐ›์•„์˜จ ์‹๋ณ„์ž๋Š” ์™ธ๋ž˜ ํ‚ค๋กœ๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์ƒˆ๋กœ์šด ์‹๋ณ„์ž๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ถ”์ฒœ ๐Ÿ‘





7. ๊ณ ๊ธ‰ ๋งคํ•‘

๋‹ค๋ฃฐ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ƒ์† ๊ด€๊ณ„ ๋งคํ•‘: ๊ฐ์ฒด์˜ ์ƒ์† ๊ด€๊ณ„๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์–ด๋–ป๊ฒŒ ๋งคํ•‘ํ•˜๋Š”์ง€
  • @MappedSuperclass: ๋“ฑ๋ก์ผ, ์ˆ˜์ •์ผ ๊ฐ™์ด ์—ฌ๋Ÿฌ ์—”ํ‹ฐํ‹ฐ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋งคํ•‘ ์ •๋ณด๋งŒ ์ƒ์†๋ฐ›๊ณ  ์‹ถ์„ ๋•Œ
  • ๋ณตํ•ฉ ํ‚ค์™€ ์‹๋ณ„ ๊ด€๊ณ„ ๋งคํ•‘: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์‹๋ณ„์ž๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์ผ ๋•Œ ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ์กฐ์ธ ํ…Œ์ด๋ธ”: ์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์„ ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ์—”ํ‹ฐํ‹ฐ ํ•˜๋‚˜์— ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ” ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ๋ฒ•


7.1 ์ƒ์† ๊ด€๊ณ„ ๋งคํ•‘


์Šˆํผํƒ€์ž… ์„œ๋ธŒํƒ€์ž… ๋…ผ๋ฆฌ ๋ชจ๋ธ์„ ํ…Œ์ด๋ธ”๋กœ ๊ตฌํ˜„ํ•˜๋Š” 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•

  • ๊ฐ๊ฐ์˜ ํ…Œ์ด๋ธ”๋กœ ๋ณ€ํ™˜: JPA์—์„œ๋Š” ์กฐ์ธ ์ „๋žต์ด๋ผ ํ•œ๋‹ค.
  • ํ†ตํ•ฉ ํ…Œ์ด๋ธ”๋กœ ๋ณ€ํ™˜: JPA์—์„œ๋Š” ๋‹จ์ผ ํ…Œ์ด๋ธ” ์ „๋žต์ด๋ผ ํ•œ๋‹ค.
  • ์„œ๋ธŒํƒ€์ž… ํ…Œ์ด๋ธ”๋กœ ๋ณ€ํ™˜: JPA์—์„œ๋Š” ๊ตฌํ˜„ ํด๋ž˜์Šค๋งˆ๋‹ค ํ…Œ์ด๋ธ” ์ „๋žต์ด๋ผ ํ•œ๋‹ค.


7.1.1 ์กฐ์ธ ์ „๋žต

์—”ํ‹ฐํ‹ฐ ๊ฐ๊ฐ์„ ๋ชจ๋‘ ํ…Œ์ด๋ธ”๋กœ ๋งŒ๋“ค๊ณ  ์ž์‹ ํ…Œ์ด๋ธ”์ด ๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๋ฐ›์•„์„œ ๊ธฐ๋ณธ ํ‚ค + ์™ธ๋ž˜ ํ‚ค๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ „๋žต

@Entity
@Inheritance(strategy = InheritanceType.JOIN) // ๋ถ€๋ชจ ํด๋ž˜์Šค์— ์ƒ์† ๋งคํ•‘, ์ „๋žต ์ง€์ •
@DiscriminatorColumn(name = "DTYPE") // ๋ถ€๋ชจ ํด๋ž˜์Šค์— ๊ตฌ๋ถ„ ์ปฌ๋Ÿผ ์ง€์ •
public abstract class Item {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name; //์ด๋ฆ„
    private int price;   //๊ฐ€๊ฒฉ
}
@Entity
@DiscriminatorValue("A") // ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ๊ตฌ๋ถ„ ์ปฌ๋Ÿผ์— ์ž…๋ ฅํ•  ๊ฐ’์„ ์ง€์ •
public class Album extends Item {

    private String artist;
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;
    private String actor;
}

@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "BOOK_ID") // ๊ธฐ๋ณธ ํ‚ค ์ปฌ๋Ÿผ๋ช… ๋ณ€๊ฒฝ, ๊ธฐ๋ณธ๊ฐ’์€ ๋ถ€๋ชจ ํ…Œ์ด๋ธ” ID ์ปฌ๋Ÿผ๋ช…
public class Book extends Item {

    private String author;
    private String isbn;
}

์žฅ์ 

  • ํ…Œ์ด๋ธ”์ด ์ •๊ทœํ™”๋œ๋‹ค.
  • ์™ธ๋ž˜ ํ‚ค ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ ์ œ์•ฝ์กฐ๊ฑด์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ €์žฅ๊ณต๊ฐ„์„ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

๋‹จ์ 

  • ์กฐํšŒํ•  ๋•Œ ์กฐ์ธ์ด ๋งŽ์ด ์‚ฌ์šฉ๋˜์–ด ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์กฐํšŒ ์ฟผ๋ฆฌ๊ฐ€ ๋ณต์žกํ•˜๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ๋“ฑ๋กํ•  ๋•Œ INSERT SQL์ด ๋‘ ๋ฒˆ ์‹คํ–‰๋œ๋‹ค.

ํŠน์ง•

  • @DiscriminatorColumn ์—†์ด๋„ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ด€๋ จ ์–ด๋…ธํ…Œ์ด์…˜

  • @PrimaryKeyJoinColumn, @DiscriminatorColumn, @DiscriminatorValue



7.1.2 ๋‹จ์ผ ํ…Œ์ด๋ธ” ์ „๋žต

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // ๋ถ€๋ชจ ํด๋ž˜์Šค์— ์ƒ์† ๋งคํ•‘, ์ „๋žต ์ง€์ •
@DiscriminatorColumn(name = "DTYPE") // ๋ถ€๋ชจ ํด๋ž˜์Šค์— ๊ตฌ๋ถ„ ์ปฌ๋Ÿผ ์ง€์ •
public abstract class Item {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item {...}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {...}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {...}

์žฅ์ 

  • ์กฐ์ธ์ด ํ•„์š” ์—†์œผ๋ฏ€๋กœ ์กฐํšŒ ์„ฑ๋Šฅ์ด ๋น ๋ฅด๋‹ค.
  • ์กฐํšŒ ์ฟผ๋ฆฌ๊ฐ€ ๋‹จ์ˆœํ•˜๋‹ค.

๋‹จ์ 

  • ์ž์‹ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋งคํ•‘ํ•œ ์ปฌ๋Ÿผ์€ ๋ชจ๋‘ null์„ ํ—ˆ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ๋‹จ์ผ ํ…Œ์ด๋ธ”์— ๋ชจ๋“  ๊ฒƒ์„ ์ €์žฅํ•˜๋ฏ€๋กœ ํ…Œ์ด๋ธ”์ด ์ปค์งˆ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์ƒํ™ฉ์— ๋”ฐ๋ผ์„œ๋Š” ์กฐํšŒ ์„ฑ๋Šฅ์ด ์˜คํžˆ๋ ค ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ๋‹ค.

ํŠน์ง•

  • @DiscriminatorColumn์„ ๊ผญ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • @DiscriminatorValue๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ์œผ๋กœ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•œ๋‹ค.



7.1.3 ๊ตฌํ˜„ ํด๋ž˜์Šค๋งˆ๋‹ค ํ…Œ์ด๋ธ” ์ „๋žต

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item {...}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {...}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {...}

์ผ๋ฐ˜์ ์œผ๋กœ ์ถ”์ฒœํ•˜์ง€ ์•Š๋Š” ์ „๋žต์ด๋‹ค.

์žฅ์ 

  • ์„œ๋ธŒ ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•ด์„œ ์ฒ˜๋ฆฌํ•  ๋•Œ ํšจ๊ณผ์ ์ด๋‹ค.
  • not null ์ œ์•ฝ ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹จ์ 

  • ์—ฌ๋Ÿฌ ์ž์‹ ํ…Œ์ด๋ธ”์„ ํ•จ๊ป˜ ์กฐํšŒํ•  ๋•Œ ์„ฑ๋Šฅ์ด ๋Š๋ฆฌ๋‹ค. (UNION์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค)
  • ์ž์‹ ํ…Œ์ด๋ธ”์„ ํ†ตํ•ฉํ•ด์„œ ์ฟผ๋ฆฌํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

ํŠน์ง•

  • ๊ตฌ๋ถ„ ์ปฌ๋Ÿผ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.



7.2 @MappedSuperclass

๋ถ€๋ชจ ํด๋ž˜์Šค๋Š” ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘ํ•˜์ง€ ์•Š๊ณ  ์ž์‹ ํด๋ž˜์Šค์—๊ฒŒ ๋งคํ•‘ ์ •๋ณด๋งŒ ์ œ๊ณตํ•˜๊ณ  ์‹ถ์œผ๋ฉด @MappedSuperclass์„ ์‚ฌ์šฉํ•œ๋‹ค.

// ๊ณตํ†ต ๋งคํ•‘ ์ •๋ณด ์ •์˜
@MappedSuperclass
public abstract class BaseEntity {

    @Id @GeneratedValue
    private Long id;
    private String name;
}
@Entity
public class Member extends BaseEntity {
    private String email;
}
@Entity
public class Seller extends BaseEntity {
    private String shopName;
}
  • ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์žฌ์ •์˜ ํ•˜๋ ค๋ฉด @AssociationOverrides, @AssociationOverride ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋ถ€๋ชจ๋กœ๋ถ€ํ„ฐ ๋ฌผ๋ ค๋ฐ›์€ ๋งคํ•‘ ์ •๋ณด๋ฅผ ์žฌ์ •์˜ ํ•˜๋ ค๋ฉด @AttributeOverrides, @AttributeOverride ์‚ฌ์šฉํ•œ๋‹ค.
// ๋งคํ•‘ ์ •๋ณด ์žฌ์ •์˜
// ๋ถ€๋ชจ์—๊ฒŒ ์ƒ์†๋ฐ›์€ id ์†์„ฑ์˜ ์ปฌ๋Ÿผ๋ช…์„ MEMBER_ID๋กœ ์žฌ์ •์˜
@Entity
@AttributeOverride(name = "id", column = @Column(name = "MEMBER_ID"))
public class Member extends BaseEntity {
    private String email;
}
// ๋‘˜ ์ด์ƒ์˜ ๋งคํ•‘ ์ •๋ณด ์žฌ์ •์˜
@Entity
@AttributeOverrides({
    @AttributeOverride(name = "id", column = @Column(name = "MEMBER_ID")),
    @AttributeOverride(name = "name", column = @Column(name = "MEMBER_NAME"))
})
public class Member extends BaseEntity {
    private String email;
}


@MappedSuperclass ํŠน์ง•

  • ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ž์‹ ํด๋ž˜์Šค์—๊ฒŒ ์—”ํ‹ฐํ‹ฐ์˜ ๋งคํ•‘ ์ •๋ณด๋ฅผ ์ƒ์†ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ em.find(), JPQL์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  • ์ง์ ‘ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•  ์ผ์ด ๊ฑฐ์˜ ์—†์œผ๋ฏ€๋กœ ์ถ”์ƒ ํด๋ž˜์Šค๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
  • ๋“ฑ๋ก์ผ์ž, ์ˆ˜์ •์ผ์ž, ๋“ฑ๋ก์ž, ์ˆ˜์ •์ž ๊ฐ™์€ ๊ณน์˜ค ์†์„ฑ์„ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.



7.3 ๋ณตํ•ฉ ํ‚ค์™€ ์‹๋ณ„ ๊ด€๊ณ„ ๋งคํ•‘

๋ณตํ•ฉ ํ‚ค๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์‹๋ณ„ ๊ด€๊ณ„, ๋น„์‹๋ณ„ ๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณธ๋‹ค.

7.3.1 ์‹๋ณ„ ๊ด€๊ณ„ vs ๋น„์‹๋ณ„ ๊ด€๊ณ„

์‹๋ณ„ ๊ด€๊ณ„
๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๋‚ด๋ ค๋ฐ›์•„์„œ ์ž์‹ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค + ์™ธ๋ž˜ ํ‚ค๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ด€๊ณ„๋‹ค.


๋น„์‹๋ณ„ ๊ด€๊ณ„
๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๋ฐ›์•„์„œ ์ž์‹ ํ…Œ์ด๋ธ”์˜ ์™ธ๋ž˜ ํ‚ค๋กœ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ด€๊ณ„๋‹ค.

  • ํ•„์ˆ˜์  ๋น„์‹๋ณ„ ๊ด€๊ณ„(Mandatory): ์™ธ๋ž˜ ํ‚ค์— null์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ํ•„์ˆ˜์ ์œผ๋กœ ๋งบ์–ด์•ผ ํ•œ๋‹ค.
  • ์„ ํƒ์  ๋น„์‹๋ณ„ ๊ด€๊ณ„(Optional): ์™ธ๋ž˜ ํ‚ค์— null์„ ํ—ˆ์šฉํ•œ๋‹ค. ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ์„์ง€ ๋ง์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ 
์ตœ๊ทผ์—๋Š” ๋น„์‹๋ณ„ ๊ด€๊ณ„๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์‹๋ณ„ ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ถ”์„ธ



7.3.2 ๋ณตํ•ฉ ํ‚ค: ๋น„์‹๋ณ„ ๊ด€๊ณ„ ๋งคํ•‘

JPA๋Š” ๋ณตํ•ฉ ํ‚ค๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.

  • @IdClass: ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ฐ€๊นŒ์šด ๋ฐฉ๋ฒ•
  • @EmbeddedId: ๊ฐ์ฒด์ง€ํ–ฅ์— ๊ฐ€๊นŒ์šด ๋ฐฉ๋ฒ•

@IdClass

@Entity
@IdClass(ParentId.class)
public class Parent {

    @Id
    @Column(name = "PARENT_ID1")
    private String id1;

    @Id
    @Column(name = "PARENT_ID2")
    private String id1;
}
public class ParentId implements Serializable {
    private String id1;
    private String id2;

    public ParentId() {}

    ...

    @Override
    public boolean equals (Object o) {...}

    @Override
    public int hashCode() {...}
}

@IdClass๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์‹๋ณ„์ž ํด๋ž˜์Šค๋Š” ๋‹ค์Œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•ด์•ผ ํ•œ๋‹ค.

  • ์‹๋ณ„์ž ํด๋ž˜์Šค์˜ ์†์„ฑ๋ช…๊ณผ ์—”ํ‹ฐํ‹ฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์‹๋ณ„์ž์˜ ์†์„ฑ๋ช…์ด ๊ฐ™์•„์•ผ ํ•œ๋‹ค.
  • Serializable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
  • equals, hashCode๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ์‹๋ณ„์ž ํด๋ž˜์Šค๋Š” public์ด์–ด์•ผ ํ•œ๋‹ค.

์‚ฌ์šฉ๋ฐฉ๋ฒ•

// ์ €์žฅ
Parent parent = new Parent();
parent.setId1("1");
parent.setId2("2");

em.persist(parent);
// ์กฐํšŒ
ParentId parentId = new ParentId("1","2");
Parent parent = em.find(Parent.class, parentId);


์ž์‹ ํด๋ž˜์Šค
์ž์‹ ํ…Œ์ด๋ธ”์—์„œ ์™ธ๋ž˜ ํ‚ค ๋งคํ•‘ ๋ฐฉ๋ฒ•

@Entity
public class Child {

    @Id
    private String id;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "PARENT_ID1", referencedColumnName = "PARENT_ID1"),
        @JoinColumn(name = "PARENT_ID2", referencedColumnName = "PARENT_ID2")
    })
    private Parent parent;
}




@EmbeddedId

@Entity
public class Parent {

    @EmbeddedId
    private ParentId id;
}
@Embeddable
public class ParentId implements Serializable {

    @Column(name = "PARENT_ID1")
    private String id1;
    @Column(name = "PARENT_ID2")
    private String id2;

    public ParentId() {}

    ...

    @Override
    public boolean equals (Object o) {...}

    @Override
    public int hashCode() {...}
}

@EmbeddedId๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์‹๋ณ„์ž ํด๋ž˜์Šค๋Š” ๋‹ค์Œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•ด์•ผ ํ•œ๋‹ค.

  • @Embeddable ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • Serializable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
  • equals, hashCode๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ์‹๋ณ„์ž ํด๋ž˜์Šค๋Š” public์ด์–ด์•ผ ํ•œ๋‹ค.

์‚ฌ์šฉ๋ฐฉ๋ฒ•

// ์ €์žฅ
Parent parent = new Parent();
ParentId parentId = new ParentId("1","2");
parent.setId(parentId);

em.persist(parent);
// ์กฐํšŒ
ParentId parentId = new ParentId("1","2");
Parent parent = em.find(Parent.class, parentId);



@IdClass vs @EmbeddedId

  • @IdClass: ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ฐ€๊นŒ์šด ๋ฐฉ๋ฒ•
  • @EmbeddedId: ๊ฐ์ฒด์ง€ํ–ฅ์— ๊ฐ€๊นŒ์šด ๋ฐฉ๋ฒ•
// JPQL
em.createQuery("select p.id.id1, p.id.id2 from Parent p"); // @EmbeddedId
em.createQuery("select p.id1, p.id2 from Parent p"); // @IdClass

์ฐธ๊ณ 
๋ณตํ•ฉ ํ‚ค์—๋Š” @GenerateValue๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋ณตํ•ฉ ํ‚ค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์ค‘ ํ•˜๋‚˜์—๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.




7.3.3 ๋ณตํ•ฉ ํ‚ค: ์‹๋ณ„ ๊ด€๊ณ„ ๋งคํ•‘

// ๋ถ€๋ชจ
@Entity
public class Parent {

    @Id
    @Column(name = "PARENT_ID")
    private String id;
}
// ์ž์‹
@Entity
@IdClass(ChildId.class)
public class Child {

    @Id
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;

    @Id @Column(name = "CHILD_ID")
    private String childId;
}

// ์ž์‹ ID
public class ChildId implements Serializable {
    
    private String parent;
    private String childId;
    ...์ƒ๋žต...
}
// ์†์ž
@Entity
@IdClass(GrandChildId.class)
public class GrandChild {

    @Id
    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "PARENT_ID"),
        @JoinColumn(name = "CHILD_ID")
    })
    private Child child;

    @Id @Column(name = "GRANDCHILD_ID")
    private String id;
}

// ์†์ž ID
public class GrandChildId implements Serializable {
    
    private ChildId child;
    private String id;
    ...์ƒ๋žต...
}



@EmbeddedId์™€ ์‹๋ณ„ ๊ด€๊ณ„

// ๋ถ€๋ชจ
@Entity
public class Parent {

    @Id
    @Column(name = "PARENT_ID")
    private String id;
}
// ์ž์‹
@Entity
public class Child {

    @EmbeddedId
    private ChildId id;

    @MapsId("parentId")
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;
}

// ์ž์‹ ID
@Embeddable
public class ChildId implements Serializable {
    
    private String parentId; // @MapsId("parentId")๋กœ ๋งคํ•‘
    
    @Column(name = "CHILD_ID")
    private String id;
    ...์ƒ๋žต...
}
// ์†์ž
@Entity
public class GrandChild {

    @EmbeddedId
    private GrandChildId id;

    @MapsId("childId")
    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "PARENT_ID"),
        @JoinColumn(name = "CHILD_ID")
    })
    private Child child;
}

// ์†์ž ID
@Embeddable
public class GrandChildId implements Serializable {
    
    private ChildId childId; // @MapsId("childId")๋กœ ๋งคํ•‘
    
    @Column(name = "GRANDCHILD_ID")
    private String id;
    ...์ƒ๋žต...
}



7.3.4 ๋น„์‹๋ณ„ ๊ด€๊ณ„๋กœ ๊ตฌํ˜„

// ๋ถ€๋ชจ
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;
}
// ์ž์‹
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;
}
// ์†์ž
@Entity
public class GrandChild {

    @Id @GeneratedValue
    @Column(name = "GRANDCHILD_ID")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "CHILD_ID")
    private Child child;
}

์‹๋ณ„ ๊ด€๊ณ„์˜ ๋ณตํ•ฉ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ์™€ ๋น„๊ตํ•˜๋ฉด ๋งคํ•‘๋„ ์‰ฝ๊ณ  ์ฝ”๋“œ๋„ ๋‹จ์ˆœํ•˜๋‹ค.


7.3.5 ์ผ๋Œ€์ผ ์‹๋ณ„ ๊ด€๊ณ„

์ผ๋Œ€์ผ ์‹๋ณ„ ๊ด€๊ณ„๋Š” ์ž์‹ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค ๊ฐ’์œผ๋กœ ๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค ๊ฐ’๋งŒ ์‚ฌ์šฉํ•œ๋‹ค.

// ๋ถ€๋ชจ
@Entity
public class Board {

    @Id @GeneratedValue
    @Column(name = "BOARD_ID")
    private Long id;

    @OneToOne(mappedBy = "board")
    private BoardDetail boardDetail;
}
@Entity
public class BoardDetail {

    @Id
    private Long boardId;

    // ์‹๋ณ„์ž๊ฐ€ ๋‹จ์ˆœํžˆ ์ปฌ๋Ÿผ ํ•˜๋‚˜๋ฉด @MapsId๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์†์„ฑ ๊ฐ’์€ ๋น„์›Œ๋‘๋ฉด ๋œ๋‹ค.
    @MapsId // BoardDetail.boardId ๋งคํ•‘
    @OneToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;
}


์‚ฌ์šฉ๋ฐฉ๋ฒ•

Board board = new Board();
em.persist(board);

BoardDetail boardDetail = new BoardDetail();
boardDetail.setBoard(board);
em.persist(boardDetail);



7.3.6 ์‹๋ณ„, ๋น„์‹๋ณ„ ๊ด€๊ณ„์˜ ์žฅ๋‹จ์ 

์‹๋ณ„ ๊ด€๊ณ„ ์žฅ์ 

  • ํŠน์ • ์ƒํ™ฉ์—์„œ ์กฐ์ธ ์—†์ด ํ•˜์œ„ ํ…Œ์ด๋ธ”๋งŒ์œผ๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹๋ณ„ ๊ด€๊ณ„ ๋‹จ์ 

  • ์‹๋ณ„ ๊ด€๊ณ„๋Š” ๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ์ž์‹ ํ…Œ์ด๋ธ”๋กœ ์ „ํŒŒํ•˜๋ฉด์„œ ์ž์‹ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค ์ปฌ๋Ÿผ์ด ์ ์  ๋Š˜์–ด๋‚œ๋‹ค.
    • SQL๋ณต์žก, ๊ธฐ๋ณธ ํ‚ค ์ธ๋ฑ์Šค๊ฐ€ ์ปค์ง
  • ์‹๋ณ„ ๊ด€๊ณ„๋Š” ๋ณตํ•ฉ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
  • ์‹๋ณ„ ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ธฐ๋ณธ ํ‚ค๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ์ž์—ฐ ํ‚ค ์ปฌ๋Ÿผ์„ ์กฐํ•ฉ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
    • ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์€ ์–ธ์ œ๋‚˜ ๋ณ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์‹๋ณ„ ๊ด€๊ณ„๋Š” ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๊ฐ€ ์œ ์—ฐํ•˜์ง€ ๋ชปํ•˜๋‹ค.
  • ์‹๋ณ„ ๊ด€๊ณ„๋Š” ์ผ๋Œ€์ผ ๊ด€๊ณ„๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋ณ„๋„์˜ ๋ณตํ•ฉ ํ‚ค ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    • ๊ธฐ๋ณธ ํ‚ค ๋งคํ•‘์— ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•˜๋‹ค.

๋น„์‹๋ณ„ ๊ด€๊ณ„ ์žฅ์ 

  • @GenerateValue์ฒ˜๋Ÿผ ํŽธ๋ฆฌํ•œ ๋Œ€๋ฆฌ ํ‚ค ์ƒ์„ฑ ๋ฐฉ๋ฒ• ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ์‹๋ณ„์ž ์ปฌ๋Ÿผ์ด ํ•˜๋‚˜์—ฌ์„œ ๋งคํ•‘์ด ์‰ฝ๋‹ค.

๋น„์‹๋ณ„ ๊ด€๊ณ„ ๋‹จ์ 

  • JPQL ์กฐํšŒ์‹œ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

๊ฒฐ๋ก 
๋น„์‹๋ณ„ ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๊ธฐ๋ณธ ํ‚ค๋Š” Long ํƒ€์ž…์˜ ๋Œ€๋ฆฌ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.




7.4 ์กฐ์ธ ํ…Œ์ด๋ธ”

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ 2๊ฐ€์ง€๋‹ค.

์กฐ์ธ ์ปฌ๋Ÿผ



์กฐ์ธ ํ…Œ์ด๋ธ”

์ถ”์ฒœ ๋ฐฉ๋ฒ•
๊ธฐ๋ณธ์€ ์กฐ์ธ ์ปฌ๋Ÿผ์„ ์‚ฌ์šฉํ•˜๊ณ  ํ•„์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด ์กฐ์ธ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜์ž.




7.4.1 ์ผ๋Œ€์ผ ์กฐ์ธ ํ…Œ์ด๋ธ”

// ๋ถ€๋ชจ
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;


    @OneToOne
    @JoinTable(
        name = "PARENT_CHILD",
        joinColumn = @JoinColumn(name = "PARENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private Child child;
}
// ์ž์‹
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;

    // ์–‘๋ฐฉํ–ฅ์œผ๋กœ ๋งคํ•‘ํ•˜๋ ค๋ฉด ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
    // @OneToOne(mappedBy = "child")
    // private Parent parent;
}



7.4.2 ์ผ๋Œ€๋‹ค ์กฐ์ธ ํ…Œ์ด๋ธ”

// ๋ถ€๋ชจ
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    @OneToMany
    @JoinTable(
        name = "PARENT_CHILD",
        joinColumn = @JoinColumn(name = "PARENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private List<Child> child = new ArrayList<Child>();
}
// ์ž์‹
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
}



7.4.2 ๋‹ค๋Œ€์ผ ์กฐ์ธ ํ…Œ์ด๋ธ”

// ๋ถ€๋ชจ
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    @OneToMany(mappedBy = "parent")
    private List<Child> child = new ArrayList<Child>();
}
// ์ž์‹
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;

    @ManyToOne
    @JoinTable(
        name = "PARENT_CHILD",
        joinColumn = @JoinColumn(name = "CHILD_ID"),
        inverseJoinColumns = @JoinColumn(name = "PARENT_ID")
    )
    private Parent parent;
}



7.4.4 ๋‹ค๋Œ€๋‹ค ์กฐ์ธ ํ…Œ์ด๋ธ”

// ๋ถ€๋ชจ
@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;

    @ManyToMany
    @JoinTable(
        name = "PARENT_CHILD",
        joinColumn = @JoinColumn(name = "PARENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private List<Child> child = new ArrayList<Child>();
}
// ์ž์‹
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
}




7.5 ์—”ํ‹ฐํ‹ฐ ํ•˜๋‚˜์— ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ” ๋งคํ•‘

@SecondaryTable์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•œ ์—”ํ‹ฐํ‹ฐ์— ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์„ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Entity
@Table(name="BOARD")
@SecondaryTable( 
    name = "BOARD_DETAIL",
    pkJoinColumns = @PrimaryKeyJoinColumn(name = "BOARD_DETAIL_ID")
)
public class Board {
    
    @Id @GeneratedValue
    @Column(name = "BOARD_ID")
    private Long id;

    private String title;

    @Column(name = "BOARD_DETAIL")
    private String content;
}
  • @SecondaryTable.name: ๋งคํ•‘ํ•  ๋‹ค๋ฅธ ํ…Œ์ด๋ธ” ์ด๋ฆ„
  • @SecondaryTable.pkJoinColumns: ๋งคํ•‘ํ•  ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธ ํ‚ค ์ปฌ๋Ÿผ ์†์„ฑ
// ์—ฌ๋Ÿฌ๊ฐœ ๊ฐ€๋Šฅ
@SecondaryTables({
    @SecondaryTable(name="BOARD_DETAIL"),
    @SecondaryTable(name="BOARD_FILE")
})

@SecondaryTable์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹จ ํ…Œ์ด๋ธ”๋‹น ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ๊ฐ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.






8. ํ”„๋ก์‹œ์™€ ์—ฐ๊ด€๊ด€๊ณ„ ๊ด€๋ฆฌ

  • ํ”„๋ก์‹œ์™€ ์ฆ‰์‹œ๋กœ๋”ฉ, ์ง€์—ฐ๋กœ๋”ฉ
  • ์˜์†์„ฑ ์ „์ด์™€ ๊ณ ์•„ ๊ฐ์ฒด

8.1 ํ”„๋ก์‹œ

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋“ค์ด ํ•ญ์ƒ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•จ๊ป˜ ์กฐํšŒํ•ด ๋‘๋Š” ๊ฒƒ์€ ํšจ์œจ์ ์ด์ง€ ์•Š๋‹ค.
  • JPA๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ์ง€์—ฐ ๋กœ๋”ฉ์„ ์ œ๊ณตํ•œ๋‹ค.
  • ์ง€์—ฐ ๋กœ๋”ฉ์ด๋ž€ ์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  • ์ง€์—ฐ ๋กœ๋”ฉ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด ๋Œ€์‹ ์— ๊ฐ€์งœ ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•œ๋ฐ ์ด๊ฒƒ์„ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ผ ํ•œ๋‹ค.

8.1.1 ํ”„๋ก์‹œ ๊ธฐ์ดˆ

ํ”„๋ก์‹œ์˜ ํŠน์ง•

  • ํ”„๋ก์‹œ ํด๋ž˜์Šค๋Š” ์‹ค์ œ ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ๋งŒ๋“ค์–ด์ง„๋‹ค.
  • ๋”ฐ๋ผ์„œ ์‚ฌ์šฉํ•˜๋Š” ์ž…์žฅ์—์„œ๋Š” ์ด๊ฒƒ์ด ์ง„์งœ ๊ฐ์ฒด์ธ์ง€ ํ”„๋ก์‹œ ๊ฐ์ฒด์ธ์ง€ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์‹ค์ œ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค.
  • ํ”„๋ก์‹œ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์‹ค์ œ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค.

ํ”„๋ก์‹œ ๊ฐ์ฒด์˜ ์ดˆ๊ธฐํ™”

  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์‹ค์ œ ์‚ฌ์šฉ๋  ๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•ด์„œ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • ์ด๋ฅผ ํ”„๋ก์‹œ ๊ฐ์ฒด์˜ ์ดˆ๊ธฐํ™”๋ผ ํ•œ๋‹ค.

์ค€์˜์† ์ƒํƒœ์™€ ์ดˆ๊ธฐํ™”

// em.getReference()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ๋ฅผ ๋ฏธ๋ฃฌ๋‹ค.
// MemberProxy ๋ฐ˜ํ™˜
Member member = em.getReference(Member.class, "id1");

tx.commit();
em.close(); // ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ข…๋ฃŒ
member.getName(); // ์ค€์˜์† ์ƒํƒœ์—์„œ ์ดˆ๊ธฐํ™” ์‹œ๋„
                  // org.hibernate.LazyInitializationException ๋ฐœ์ƒ



8.1.2 ํ”„๋ก์‹œ์™€ ์‹๋ณ„์ž

์—”ํ‹ฐํ‹ฐ๋ฅผ ํ”„๋ก์‹œ๋กœ ์กฐํšŒํ•  ๋•Œ ์‹๋ณ„์ž(PK) ๊ฐ’์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•˜๋Š”๋ฐ ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์ด ์‹๋ณ„์ž ๊ฐ’์„ ๋ณด๊ด€ํ•œ๋‹ค.

Team team = em.getReference(Team.class, "team1"); // ์‹๋ณ„์ž ๋ณด๊ด€
team.getId(); // ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์Œ

๋‹จ @Access(AccessType.PROPERTY)์ผ ๋•Œ๋งŒ ์œ„์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ณ , @Access(AccessType.FIELD)๋กœ ์„ค์ •ํ•˜๋ฉด ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค.


8.1.3 ํ”„๋ก์‹œ ํ™•์ธ

JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” PersistenceUnitUtil.isLoaded(Object entity) ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค์˜ ์ดˆ๊ธฐํ™” ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

boolean isLoad = em.getEntityManagerFactory()
                .getPersistenceUnitUtil().isLoaded(entity);




8.2 ์ฆ‰์‹œ ๋กœ๋”ฉ๊ณผ ์ง€์—ฐ ๋กœ๋”ฉ

JPA๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ์˜ ์กฐํšŒ ์‹œ์ ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.

  • ์ฆ‰์‹œ ๋กœ๋”ฉ: ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์กฐํšŒํ•œ๋‹ค.
    • ์„ค์ • ๋ฐฉ๋ฒ•: @ManyToOne(fetch = FetchType.EAGER)
  • ์ง€์—ฐ ๋กœ๋”ฉ: ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹ค์ œ ์‚ฌ์šฉํ•  ๋•Œ ์กฐํšŒํ•œ๋‹ค.
    • ์„ค์ • ๋ฐฉ๋ฒ•: @ManyToOne(fetch = FetchType.LAZY)


8.2.1 ์ฆ‰์‹œ ๋กœ๋”ฉ

์ฆ‰์‹œ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @ManyToOne(fetch = FetchType.EAGER)๋กœ ์ง€์ •ํ•œ๋‹ค.

@Entity
public class Member {
    ...์ƒ๋žต...
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ...์ƒ๋žต...
}



8.2.2 ์ง€์—ฐ ๋กœ๋”ฉ

์ง€์—ฐ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @ManyToOne(fetch = FetchType.LAZY)๋กœ ์ง€์ •ํ•œ๋‹ค. ์ง€์—ฐ ๋กœ๋”ฉ์€ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•œ๋‹ค.

@Entity
public class Member {
    ...์ƒ๋žต...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ...์ƒ๋žต...
}

์ฐธ๊ณ 
์กฐํšŒ ๋Œ€์ƒ์ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ด๋ฏธ ์žˆ์œผ๋ฉด ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ด์œ ๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.


8.2.3 ์ฆ‰์‹œ ๋กœ๋”ฉ, ์ง€์—ฐ ๋กœ๋”ฉ ์ •๋ฆฌ

  • ์ง€์—ฐ ๋กœ๋”ฉ: ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ”„๋ก์‹œ๋กœ ์กฐํšŒํ•œ๋‹ค. ํ”„๋ก์‹œ๋ฅผ ์‹ค์ œ ์‚ฌ์šฉํ•  ๋•Œ ์ดˆ๊ธฐํ™”ํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • ์ฆ‰์‹œ ๋กœ๋”ฉ: ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฆ‰์‹œ ์กฐํšŒํ•œ๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ๊ฐ€๋Šฅํ•˜๋ฉด SQL ์กฐ์ธ์„ ์‚ฌ์šฉํ•ด์„œ ํ•œ ๋ฒˆ์— ์กฐํšŒํ•œ๋‹ค.



8.3 ์ง€์—ฐ ๋กœ๋”ฉ ํ™œ์šฉ

์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ž์ฃผ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์•„๋‹Œ์ง€์— ๋”ฐ๋ผ ์ฆ‰์‹œ ๋กœ๋”ฉ, ์ง€์—ฐ ๋กœ๋”ฉ์„ ๊ฒฐ์ •ํ•œ๋‹ค.

8.3.1 ํ”„๋ก์‹œ์™€ ์ปฌ๋ ‰์…˜ ๋ž˜ํผ

์ปฌ๋ ‰์…˜์˜ ์ง€์—ฐ ๋กœ๋”ฉ

Member member = em.find(Member.class, "member1");
List<Order> = member.getOrders();
System.out.println(orders.getClass().getName()); 

// ์ถœ๋ ฅ
// org.hibernate.collection.internal.PersistenBag



8.3.2 JPA ๊ธฐ๋ณธ ํŽ˜์น˜ ์ „๋žต

  • @ManyToOne, @OneToOne: ์ฆ‰์‹œ ๋กœ๋”ฉ(FetchType.EAGER)
  • @OneToMany, @ManyToMany: ์ง€์—ฐ ๋กœ๋”ฉ(FetchType.LAZY)

์ถ”์ฒœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ์—ฐ๊ด€๊ด€๊ณ„์— ์ง€์—ฐ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์ฆ‰์‹œ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•œ๋‹ค.



8.3.3 ์ปฌ๋ ‰์…˜์— FetchType.EAGER ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

์ปฌ๋ ‰์…˜์— FetchType.EAGER๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์ฃผ์˜ํ•  ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ปฌ๋ ‰์…˜์„ ์ฆ‰์‹œ ๋กœ๋”ฉํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • ๋„ˆ๋ฌด ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์ปฌ๋ ‰์…˜ ์ฆ‰์‹œ ๋กœ๋”ฉ์€ ํ•ญ์ƒ OUTER JOIN์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • @ManyToOne, @OneToOne
    • (optional = false): ๋‚ด๋ถ€ ์กฐ์ธ
    • (optional = true): ์™ธ๋ถ€ ์กฐ์ธ
  • @OneToMany, @ManyToMany
    • (optional = false): ์™ธ๋ถ€ ์กฐ์ธ
    • (optional = true): ๋‚ด๋ถ€ ์กฐ์ธ


8.4 ์˜์†์„ฑ ์ „์ด: CASCADE

ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์—ฐ์† ์ƒํƒœ๋กœ ๋งŒ๋“ค ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์œผ๋ฉด ์˜์†์„ฑ ์ „์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค. JPA์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ์—ฐ๊ด€๋œ ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋Š” ์˜์† ์ƒํƒœ์—ฌ์•ผ ํ•œ๋‹ค.

8.4.1 ์˜์†์„ฑ ์ „์ด: ์ €์žฅ

@Entity
public class Parent {
    ...์ƒ๋žต...
    @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
    private List<Child> children = new ArrayList<Child>();
    ...์ƒ๋žต...
}
Parent parent = new Parent();
Child child = new Child();

child.setParent(parent);
parent.getChildren().add(child);

// Parent๋งŒ ์ €์žฅํ–ˆ์ง€๋งŒ, ์˜์†์„ฑ ์ „์ด์— ์˜ํ•ด Child๋„ ์ €์žฅ๋จ
em.persist(parent);

์˜์†์„ฑ ์ „์ด๋Š” ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ๊ณผ๋Š” ์•„๋ฌด ๊ด€๋ จ์ด ์—†๋‹ค. ๋‹จ์ง€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†ํ™”ํ•  ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ๊ฐ™์ด ์˜์†ํ™”ํ•˜๋Š” ํŽธ๋ฆฌํ•จ์„ ์ œ๊ณตํ•  ๋ฟ์ด๋‹ค.

8.4.2 ์˜์†์„ฑ ์ „์ด: ์‚ญ์ œ

@Entity
public class Parent {
    ...์ƒ๋žต...
    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private List<Child> children = new ArrayList<Child>();
    ...์ƒ๋žต...
}
Parent parent = em.find(Parent.class, 1L);

// Parent๋งŒ ์‚ญ์ œํ–ˆ์ง€๋งŒ, ์˜์†์„ฑ ์ „์ด์— ์˜ํ•ด Child๋„ ์‚ญ์ œ๋จ
em.remove(parent);


8.4.3 CASCADE์˜ ์ข…๋ฅ˜

public enum CascadeType {
    ALL,        // ๋ชจ๋‘ ์ ์šฉ
    PERSIST,    // ์˜์†
    MERGE,      // ๋ณ‘ํ•ฉ
    REMOVE,     // ์‚ญ์ œ
    REFRESH,
    DETACH
}




8.5 ๊ณ ์•„ ๊ฐ์ฒด

JPA๋Š” ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ๋Š์–ด์ง„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ ์ด๊ฒƒ์„ ๊ณ ์•„ ๊ฐ์ฒด(ORPHAN) ์ œ๊ฑฐ๋ผ ํ•œ๋‹ค. ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์˜ ์ปฌ๋ ‰์…˜์—์„œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ์˜ ์ฐธ์กฐ๋งŒ ์ œ๊ฑฐํ•˜๋ฉด ์ž์‹ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ž๋™์œผ๋กœ ์‚ญ์ œ๋œ๋‹ค.

@Entity
public class Parent {
    ...์ƒ๋žต...
    @OneToMany(mapeedBy = "parent", orphanRemoval = true)
    private List<Child> children = new ArrayList<Child>();
    ...์ƒ๋žต...
}
Parent parent = em.find(Parent.class, id);
parent.getChildren().remove(0); // ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ปฌ๋ ‰์…˜์—์„œ ์‚ญ์ œ

// SQL
DELETE FROM CHILD WHERE ID=?

๊ณ ์•„ ๊ฐ์ฒด ์ œ๊ฑฐ๋Š” ์ฐธ์กฐ๊ฐ€ ์ œ๊ฑฐ๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ๋‹ค๋ฅธ ๊ณณ์—์„œ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š” ๊ณ ์•„ ๊ฐ์ฒด๋กœ ๋ณด๊ณ  ์‚ญ์ œํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. ๋˜, ๋ถ€๋ชจ๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ์ž์‹๋„ ๊ฐ™์ด ์ œ๊ฑฐ๋œ๋‹ค.


8.6 ์˜์†์„ฑ ์ „์ด + ๊ณ ์•„ ๊ฐ์ฒด, ์ƒ๋ช… ์ฃผ๊ธฐ

CascadeType.All + orphanRemoval = true๋ฅผ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? ๋‘ ์˜ต์…˜์„ ๋ชจ๋‘ ํ™œ์„ฑํ™” ํ•˜๋ฉด ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ†ตํ•ด์„œ ์ž์‹์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์ž์‹์„ ์ €์žฅํ•˜๋ ค๋ฉด ๋ถ€๋ชจ์— ๋“ฑ๋กํ•˜๋ฉด ๋œ๋‹ค (CASCADE)
  • ์ž์‹์„ ์‚ญ์ œํ•˜๋ ค๋ฉด ๋ถ€๋ชจ์—์„œ ์ œ๊ฑฐํ•˜๋ฉด ๋œ๋‹ค (orphanRemoval)





9. ๊ฐ’ ํƒ€์ž…

JPA์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…

  • ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…
    • @Entity
  • ๊ฐ’ ํƒ€์ž…
    • ๊ธฐ๋ณธ๊ฐ’ ํƒ€์ž…
      • ์ž๋ฐ” ๊ธฐ๋ณธ ํƒ€์ž…(์˜ˆ: int, double)
      • ๋ž˜ํผ ํด๋ž˜์Šค(์˜ˆ: Integer)
      • String
    • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…
      • embedded type
    • ์ปฌ๋ ‰์…˜ ๊ฐ’ ํƒ€์ž…
      • collection value type


9.1 ๊ธฐ๋ณธ๊ฐ’ ํƒ€์ž…

private String name;
private int age;
...


9.2 ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…(๋ณตํ•ฉ ๊ฐ’ ํƒ€์ž…)

@Entity
public class Member {
    @Embedded Address homeAddress;
}
@Embeddable
public class Address {
    @Column(name="city")
    private String city;
    private String street;
    private String zipcode;
    ...
}
  • @Embeddable: ๊ฐ’ ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ๊ณณ์— ํ‘œ์‹œ
  • @Embedded: ๊ฐ’ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์— ํ‘œ์‹œ


9.2.1 ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…๊ณผ ํ…Œ์ด๋ธ” ๋งคํ•‘

UML์—์„œ ์ž„๋ฒ ๋””๋“œ ๊ฐ’ ํƒ€์ž…์€ ๋‹จ์ˆœํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์ด ํŽธ๋ฆฌํ•˜๋‹ค.


9.2.2 ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…๊ณผ ์—ฐ๊ด€๊ด€๊ณ„

@Entity
public class Member {
    @Embedded Address address;
    @Embedded PhoneNumber phoneNumber;
}

@Embeddable
public class Address {
    String city;
    String street;
    String state;
    @Embedded Zipcode zipcode;
}

@Embeddable
public class Zipcode {
    String zip;
    String plusFour;
}

@Embeddable
public class PhoneNumber {
    String areaCode;
    String localNumber;
    @ManyToOne PhoneServiceProvider provider;
}

@Entity
public class PhoneServiceProvider {
    @Id String name;
}


9.2.3 @AttributeOverride: ์†์„ฑ ์žฌ์ •์˜

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์— ์ •์˜ํ•œ ๋งคํ•‘์ •๋ณด๋ฅผ ์žฌ์ •์˜ ํ•˜๋ ค๋ฉด ์—”ํ‹ฐํ‹ฐ์— @AttributeOverride๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

@Entity
public class Member {
    @Embedded
    private Address address;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(ame="city", column=@Column(name="COMPANY_CITY")),
        @AttributeOverride(name="street", column=@Column(name="COMPANY_STREET")),
        @AttributeOverride(name="zipcode", column=@Column(name="COMPANY_ZIPCODE"))
    })
    private Address companyAddress;
}


9.2.4 ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…๊ณผ null

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์ด null์ด๋ฉด ๋งคํ•‘ํ•œ ์ปฌ๋Ÿผ ๊ฐ’์€ ๋ชจ๋‘ null์ด ๋œ๋‹ค.

member.setAddress(null);
em.persist(member);
// ํšŒ์› ํ…Œ์ด๋ธ”์˜ ์ฃผ์†Œ์™€ ๊ด€๋ จ๋œ CITY,STREET,ZIPCODE ์ปฌ๋Ÿผ ๊ฐ’์€ ๋ชจ๋‘ null์ด ๋œ๋‹ค.




9.3 ๊ฐ’ ํƒ€์ž…๊ณผ ๋ถˆ๋ณ€ ๊ฐ์ฒด

๊ฐ’ ํƒ€์ž…์€ ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ์กฐ๊ธˆ์ด๋ผ๋„ ๋‹จ์ˆœํ™”ํ•˜๋ ค๊ณ  ๋งŒ๋“  ๊ฐœ๋…์ด๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐ’ ํƒ€์ž…์€ ๋‹จ์ˆœํ•˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

9.3.1 ๊ฐ’ ํƒ€์ž… ๊ณต์œ  ์ฐธ์กฐ

์ž„๋ฒ ๋””๋“œ ํƒ€์ž… ๊ฐ™์€ ๊ฐ’ ํƒ€์ž…์„ ์—ฌ๋Ÿฌ ์—”ํ‹ฐํ‹ฐ์—์„œ ๊ณต์œ ํ•˜๋ฉด ์œ„ํ•จํ•˜๋‹ค.

member1.setAddress(new Address("OldCity"));
Address address = member1.getAddress();

address.setCity("NewCity");
member2.setAddress(address);

// ํšŒ์›2์˜ ์ฃผ์†Œ๋งŒ NewCity๋กœ ๋ณ€๊ฒฝ๋˜๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•˜์ง€๋งŒ ํšŒ์›1์˜ ์ฃผ์†Œ๋„ NewCity๋กœ ๋ณ€๊ฒฝ๋˜์–ด ๋ฒ„๋ฆฐ๋‹ค.


9.3.2 ๊ฐ’ ํƒ€์ž… ๋ณต์‚ฌ

๊ฐ’ ํƒ€์ž…์˜ ์‹ค์ œ ์ธ์Šคํ„ด์Šค์ธ ๊ฐ’์„ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์€ ์œ„ํ—˜ํ•˜๋‹ค. ๋Œ€์‹ ์— ๊ฐ’(์ธ์Šคํ„ด์Šค)์„ ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

member1.setAddress(new Address("OldCity"));
Address address = member1.getAddress();

// ๋ณต์‚ฌ
Address newAddress = address.clone();
newAddress.setCity("NewCity");
member2.setAddress(newAddress);


9.3.3 ๋ถˆ๋ณ€ ๊ฐ์ฒด

๊ฐ’ ํƒ€์ž…์€ ๋ถ€์ž‘์šฉ ๊ฑฑ์ • ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๊ฐ์ฒด๋ฅผ ๋ถˆ๋ณ€ํ•˜๊ฒŒ ๋งŒ๋“ค๋ฉด ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๋ถ€์ž‘์šฉ์„ ์›์ฒœ ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์ฃผ์†Œ ๋ถˆ๋ณ€ ๊ฐ์ฒด
@Embeddable
public class Address {
    private String city;
    
    protected Address () {}

    public Address(String city) {
        this.city = city;
    }
    

    public String getCity() {
        return city;
    }

    // Setter๋Š” ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.
}



9.4 ๊ฐ’ ํƒ€์ž…์˜ ๋น„๊ต

  • ๋™์ผ์„ฑ ๋น„๊ต: ์ธ์Šคํ„ด์Šค์˜ ์ฐธ์กฐ ๊ฐ’์„ ๋น„๊ต, == ์‚ฌ์šฉ
  • ๋™๋“ฑ์„ฑ ๋น„๊ต: ์ธ์Šคํ„ด์Šค์˜ ๊ฐ’์„ ๋น„๊ต, equals() ์‚ฌ์šฉ
  • ๊ฐ’ ํƒ€์ž…์„ ๋น„๊ตํ•  ๋•Œ๋Š” equals()๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋™๋“ฑ์„ฑ ๋น„๊ต๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ฐ’ ํƒ€์ž…์˜ equals(), hashCode() ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.


9.5 ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜

๊ฐ’ ํƒ€์ž…์„ ํ•˜๋‚˜ ์ด์ƒ ์ปฌ๋ ‰์…˜์— ๋ณด๊ด€ํ•˜๊ณ  @ElementCollection, @CollectionTable ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;

    @ElementCollection
    @CollectionTable(
        name = "FAVORITE_FOODS",
        joinColumns = @JoinColumn(name = "MEMBER_ID")
    )
    @Column(name = "FOOD_NAME")
    private Set<String> favoriteFoods = new HashSet<String>();

    @ElementCollection
    @CollectionTable(
        name = "ADDRESS",
        joinColumns = @JoinColumn(name = "MEMBER_ID")
    )
    private List<Address> addressHistory = new ArrayList<Address>();
}
@Embeddable
public class Address {

    @Column
    private String city;
    private String street;
    private String zipcode;
}


9.5.1 ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ์‚ฌ์šฉ

๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์€ ์˜์†์„ฑ ์ „์ด(Cascade) + ๊ณ ์•„ ๊ฐ์ฒด ์ œ๊ฑฐ(Orphan remove) ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.

// ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜๋„ ์กฐํšŒํ•  ๋•Œ ํŽ˜์น˜ ์ „๋žต์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.
@ElementCollection(fetch = FetchType.LAZY) // ๊ธฐ๋ณธ๊ฐ’ LAZY


9.5.2 ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์˜ ์ œ์•ฝ์‚ฌํ•ญ

์ œ์•ฝ์‚ฌํ•ญ

  • ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์— ๋ณด๊ด€๋œ ๊ฐ’ ํƒ€์ž…๋“ค์€ ๋ณ„๋„์˜ ํ…Œ์ด๋ธ”์— ๋ณด๊ด€ํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์—ฌ๊ธฐ์— ๋ณด๊ด€๋œ ๊ฐ’ ํƒ€์ž…์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ๊ธฐ ์–ด๋ ต๋‹ค.
  • ์ด๋Ÿฐ ๋ฌธ์ œ๋กœ JPA ๊ตฌํ˜„์ฒด๋“ค์€ ๊ฐ’ ํƒ€์ž… ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•˜๋ฉด ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์ด ๋งคํ•‘๋œ ํ…Œ์ด๋ธ”์˜ ์—ฐ๊ด€๋œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œ, ํ˜„์žฌ ๊ฐ’์œผ๋กœ ์ €์žฅํ•œ๋‹ค.
  • ์„ฑ๋Šฅ ๋ฌธ์ œ ๋ฐœ์ƒ
  • ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์„ ๋งคํ•‘ํ•˜๋Š” ํ…Œ์ด๋ธ”์€ ๋ชจ๋“  ์ปฌ๋Ÿผ์„ ๋ฌถ์–ด์„œ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๊ตฌ์„ฑ ํ•ด์•ผํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ณธ ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด์œผ๋กœ ์ธํ•ด ์ปฌ๋Ÿผ์— null์„ ์ž…๋ ฅํ•  ์ˆ˜ ์—†๊ณ , ๊ฐ’์€ ๊ฐ’์„ ์ค‘๋ณตํ•ด์„œ ์ €์žฅํ•  ์ˆ˜ ์—†๋Š” ์ œ์•ฝ๋„ ์žˆ๋‹ค.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

  • ๋”ฐ๋ผ์„œ ์‹ค๋ฌด์—์„œ๋Š” ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์ด ๋งคํ•‘๋œ ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ๋‹ค๋ฉด ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ๋Œ€์‹ ์— ์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ์˜์†์„ฑ ์ „์ด + ๊ณ ์•„ ๊ฐ์ฒด ์ œ๊ฑฐ ๊ธฐ๋Šฅ์„ ์ ์šฉํ•˜๋ฉด ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
// ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ๋Œ€์‹ ์— ์ผ๋Œ€๋‹ค ๊ด€๊ณ„ ์‚ฌ์šฉ
@Entity
public class AddressEntity {

    @Id @GeneratedValue
    private Long id;

    @Embedded
    private Address address;
}

@Embeddable
public class Address {

    @Column
    private String city;
    private String street;
    private String zipcode;
}
// ์„ค์ • ์ฝ”๋“œ
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<AddressEntity>();



9.6 ์ •๋ฆฌ

์—”ํ‹ฐํ‹ฐ ํƒ€์ž…์˜ ํŠน์ง•

  • ์‹๋ณ„์ž(@Id)๊ฐ€ ์žˆ๋‹ค.
  • ์ƒ๋ช… ์ฃผ๊ธฐ๊ฐ€ ์žˆ๋‹ค.
    • ์ƒ์„ฑ, ์˜์†, ์†Œ๋ฉธ
  • ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์ฐธ์กฐ ๊ฐ’์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด ํšŒ์› ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ์—์„œ ํšŒ์› ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ’ ํƒ€์ž…์˜ ํŠน์ง•

  • ์‹๋ณ„์ž๊ฐ€ ์—†๋‹ค
  • ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ์—”ํ‹ฐํ‹ฐ์— ์˜์กดํ•œ๋‹ค.
    • ์˜์กดํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ๊ฐ™์ด ์ œ๊ฑฐ๋œ๋‹ค.
  • ๊ณต์œ ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค.
    • ๋Œ€์‹ ์— ๊ฐ’์„ ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์˜ค์ง ํ•˜๋‚˜์˜ ์ฃผ์ธ๋งŒ์ด ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
    • ๋ถˆ๋ณ€ ๊ฐ์ฒด๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค.




10. ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด

10.1 ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์†Œ๊ฐœ

JPQL ์ด๋ž€?

  • ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ๋‹ค
  • SQL์„ ์ถ”์ƒํ™”ํ•ด์„œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค SQL์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • SQL์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ์˜ ์ฟผ๋ฆฌ๋ผ๋ฉด JPQL์€ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ๋‹ค.

JPA๋Š” JPQL๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค์–‘ํ•œ ๊ฒ€์ƒ‰ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.

๊ณต์‹ ์ง€์› ๊ธฐ๋Šฅ

  • JPQL(Java Persistence Query Language)
  • Criteria ์ฟผ๋ฆฌ: JPA์„ ํŽธํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” API, ๋นŒ๋” ํด๋ž˜์Šค ๋ชจ์Œ
  • ๋„ค์ดํ‹ฐ๋ธŒ SQL: JPA์—์„œ JPQL ๋Œ€์‹  ์ง์ ‘ SQL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋น„ ๊ณต์‹ ๊ธฐ๋Šฅ

  • QueryDSL: Criteria ์ฟผ๋ฆฌ์ฒ˜๋Ÿผ JPQL์„ ํŽธํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ๋นŒ๋” ํด๋ž˜์Šค ๋ชจ์Œ, ๋น„ํ‘œ์ค€ ์˜คํ”ˆ์†Œ์Šค ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.
  • JDBC ์ง์ ‘ ์‚ฌ์šฉ
  • MyBatis ๊ฐ™์€ SQL ๋งคํผ ํ”„๋ ˆ์ž„์›Œํฌ ์‚ฌ์šฉ


10.1.1 JPQL ์†Œ๊ฐœ

  • JPQL์€ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ๋‹ค.
  • JPQL์€ SQL์„ ์ถ”์ƒํ™”ํ•ด์„œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • ๋ฐฉ์–ธ๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋œ๋‹ค.
  • JPQL์€ SQL๋ณด๋‹ค ๊ฐ„๊ฒฐํ•˜๋‹ค.
    • ์—”ํ‹ฐํ‹ฐ ์ง์ ‘ ์กฐํšŒ
    • ๋ฌต์‹œ์  ์กฐ์ธ
    • ๋‹คํ˜•์„ฑ ์ง€์›
@Entity(name="Member")
public class Member {
    ...์ƒ๋žต...
    @Column(name = "name")
    private String name;
    ...์ƒ๋žต...
}
// JPQL ์‚ฌ์šฉ
String jpql = "select m from Member as m where m.username = 'kim'";
List<Member> resultList = em.createQuery(jpql, Member.class).getResultList();
-- ์‹ค์ œ ์‹คํ–‰๋œ SQL, ๋ณ„์นญ์€ ๋„ˆ๋ฌด ๋ณต์žกํ•ด ์ž„์˜ ์ˆ˜์ •
select
    member.id as id,
    member.age as age,
    member.team_id as team,
    member.name as name
from
    Member member
where
    member.name='kim'



10.1.2 Criteria ์†Œ๊ฐœ

Criteria๋Š” JPQL์„ ์ƒ์„ฑํ•˜๋Š” ๋นŒ๋” ํด๋ž˜์Šค๋‹ค. Criteria์˜ ์žฅ์ ์€ ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ query.select(m).where(...)์ฒ˜๋Ÿผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฝ”๋“œ๋กœ JPQL์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.

์žฅ์ 

  • ์ปดํŒŒ์ผ ์‹œ์ ์— ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • IDE๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ ์ž๋™์™„์„ฑ์„ ์ง€์›ํ•œ๋‹ค.
  • ๋™์  ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ํŽธํ•˜๋‹ค.
// Criteria ์‚ฌ์šฉ ์ค€๋น„
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);

// ๋ฃจํŠธ ํด๋ž˜์Šค(์กฐํšŒ๋ฅผ ์‹œ์ž‘ํ•  ํด๋ž˜์Šค)
Root<Member> m = query.from(Member.class);

// ์ฟผ๋ฆฌ ์ƒ์„ฑ
CriteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("username"), "kim"));
List<Member> resultList = em.createQuery(cq).getResultList();

Criteria๊ฐ€ ๊ฐ€์ง„ ์žฅ์ ์ด ๋งŽ์ง€๋งŒ ๋ชจ๋“  ์žฅ์ ์„ ์ƒ์‡„ํ•  ์ •๋„๋กœ ๋ณต์žกํ•˜๊ณ  ์žฅํ™ฉํ•˜๋‹ค.


10.1.3 QueryDSL ์†Œ๊ฐœ

  • QueryDSL๋„ Criteria์ฒ˜๋Ÿผ JPQL ๋นŒ๋” ์—ญํ• ์„ ํ•œ๋‹ค.
  • ์ฝ”๋“œ ๊ธฐ๋ฐ˜์ด๋‹ค
  • ๋‹จ์ˆœํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๋‹ค
  • ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋„ JPQL๊ณผ ๋น„์Šทํ•ด์„œ ํ•œ๋ˆˆ์— ๋“ค์–ด์˜จ๋‹ค
// ์ค€๋น„
JPAQuery query = new JPAQuery(em);
QMember member = QMember.member;

// ์ฟผ๋ฆฌ, ๊ฒฐ๊ณผ์กฐํšŒ
List<Member> members = query.from(member)
                    .where(member.username.eq("kim"))
                    .list(member);



10.1.4 ๋„ค์ดํ‹ฐ๋ธŒ SQL ์†Œ๊ฐœ

JPA๋Š” SQL์„ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š”๋ฐ ์ด๊ฒƒ์„ ๋„ค์ดํ‹ฐ๋ธŒ SQL์ด๋ผ ํ•œ๋‹ค.

String sql = "SELECT ID, AGE, TEAM_ID, NAME FROM MEMBER WHERE NAME = 'kim'";
List<Member> resultList = em.createNativeQuery(sql, Member.class).getResultList();

๋„ค์ดํ‹ฐ๋ธŒ SQL์˜ ๋‹จ์ ์€ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์˜์กดํ•˜๋Š” SQL์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ๋„ค์ดํ‹ฐ๋ธŒ SQL๋„ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.


10.1.5 JDBC ์ง์ ‘ ์‚ฌ์šฉ, ๋งˆ์ด๋ฐ”ํ‹ฐ์Šค ๊ฐ™์€ SQL ๋งคํผ ํ”„๋ ˆ์ž„์›Œํฌ ์‚ฌ์šฉ

๋‹ค์Œ์€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ ์ง์ ‘ JDBC Connection์„ ํš๋“ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {

    @Override
    public void execute(Connection connection) throw SQLException {...}
});
  • JDBC๋‚˜ ๋งˆ์ด๋ฐ”ํ‹ฐ์Šค๋ฅผ JPA์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ ์ ˆํ•œ ์‹œ์ ์— ๊ฐ•์ œ๋กœ ํ”Œ๋Ÿฌ์‹œํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋™๊ธฐํ™” ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.



10.2 JPQL

JPQL ํŠน์ง•

  • JPQL์€ ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด๋‹ค. ๋”ฐ๋ผ์„œ ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌํ•œ๋‹ค.
  • JPQL์€ SQL์„ ์ถ”์ƒํ™”ํ•ด์„œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค SQL์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • JPQL์€ ๊ฒฐ๊ตญ SQL๋กœ ๋ณ€ํ™˜๋œ๋‹ค.

10.2.1 ๊ธฐ๋ณธ ๋ฌธ๋ฒ•๊ณผ ์ฟผ๋ฆฌ API

JPQL ๋ฌธ๋ฒ•

select_๋ฌธ :: =
    select_์ ˆ
    from_์ ˆ
    [where_์ ˆ]
    [groupby_์ ˆ]
    [having_์ ˆ]
    [orderby_์ ˆ]

update_๋ฌธ :: update_์ ˆ [where_์ ˆ]
delete_๋ฌธ :: delete_์ ˆ [where_์ ˆ]


SELECT ๋ฌธ

SELECT m FROM Member AS m where m.username = 'Hello'
  • ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„
    • ์—”ํ‹ฐํ‹ฐ์™€ ์†์„ฑ์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค.
    • JPQL ํ‚ค์›Œ๋“œ(SELECT, FROM, AS..)๋Š” ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„
    • ํ…Œ์ด๋ธ” ๋ช…์ด ์•„๋‹ˆ๋ผ ์—”ํ‹ฐํ‹ฐ ๋ช…์„ ์‚ฌ์šฉํ•œ๋‹ค.
    • @Entity(name=โ€xxxโ€)๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์—”ํ‹ฐํ‹ฐ ๋ช…์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ํด๋ž˜์Šค๋ช…์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋ณ„์นญ์€ ํ•„์ˆ˜
    • JPQL์€ ๋ณ„์นญ์„ ํ•„์ˆ˜๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. (JPA ๋ช…์„ธ๋กœ ์น˜๋ฉด ์ •ํ™•ํžˆ๋Š” ์‹๋ณ„ ๋ณ€์ˆ˜)
    • AS๋Š” ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.


TypeQuery, Query
์ž‘์„ฑํ•œ JPQL์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด ์ฟผ๋ฆฌ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

  • TypeQuery ๊ฐ์ฒด: ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ์‚ฌ์šฉ
  • Query ๊ฐ์ฒด: ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ง€์ •ํ•  ์ˆ˜ ์—†์œผ๋ฉด ์‚ฌ์š”
// TypeQuery ์‚ฌ์šฉ

TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);

List<Member> resultList = query.getResultList();
for (Member member : resultList) {
    ...
}
// Query ์‚ฌ์šฉ

Query query = em.createQuery("SELECT m FROM Member m");

List resultList = query.getResultList();
for (Object o : resultList) {
    Object[] result = (Object[]) o; // ๊ฒฐ๊ณผ๊ฐ€ ๋‘˜ ์ด์ƒ์ด๋ฉด Object[] ๋ฐ˜ํ™˜
    System.out.println("username = " + result[0]);
    System.out.println("age = " + result[1]);
}


๊ฒฐ๊ณผ ์กฐํšŒ
๋‹ค์Œ ๋ฉ”์†Œ๋“œ๋“ค์„ ํ˜ธ์ถœํ•˜๋ฉด ์‹ค์ œ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•œ๋‹ค.

  • query.getResultList(): ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜. ๋งŒ์•ฝ ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ๋นˆ ์ปฌ๋ ‰์…˜ ๋ฐ˜ํ™˜
  • query.getSingleResult(): ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํžˆ ํ•˜๋‚˜์ผ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    • ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด javax.persistence.NoResultException ๋ฐœ์ƒ
    • ๊ฒฐ๊ณผ๊ฐ€ 1๊ฐœ๋ณด๋‹ค ๋งŽ์œผ๋ฉด javax.persistence.NonUniqueResultException ๋ฐœ์ƒ



10.2.2 ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ

JDBC๋Š” ์œ„์น˜ ๊ธฐ์ค€ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ๋งŒ ์ง€์›ํ•˜์ง€๋งŒ JPQL์€ ์ด๋ฆ„ ๊ธฐ์ค€ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ๋„ ์ง€์›ํ•œ๋‹ค.

์ด๋ฆ„ ๊ธฐ์ค€ ํŒŒ๋ผ๋ฏธํ„ฐ

String usernameParam = "user1";

// ํŒŒ๋ผ๋ฏธํ„ฐ ์•ž์— :์„ ๋ถ™์ธ๋‹ค.
TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m where m.username = :username", Member.class);
query.setParameter("username", usernameParam);

List<Member> resultList = query.getResultList();


์œ„์น˜ ๊ธฐ์ค€ ํŒŒ๋ผ๋ฏธํ„ฐ

String usernameParam = "user1";

// ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ? ๋‹ค์Œ์— ์œ„์น˜ ๊ฐ’์„ ์ฃผ๋ฉด ๋œ๋‹ค.
List<Member> members = em.createQuery("SELECT m FROM Member m where m.username = ?1", Member.class);

query.setParameter(1, usernameParam);
List<Member> resultList = query.getResultList();

๊ฒฝ๊ณ 
ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ง์ ‘ JPQL์„ ๋งŒ๋“ค๋ฉด SQL ์ธ์ ์…˜ ๊ณต๊ฒฉ์„ ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.

"SELECT m FROM Member m where m.username = '" + usernameParam + "'"


10.2.3 ํ”„๋กœ์ ์…˜

SELECT ์ ˆ์— ์กฐํšŒํ•  ๋Œ€์ƒ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์„ ํ”„๋กœ์ ์…˜์ด๋ผ๊ณ  ํ•œ๋‹ค. [SELECT {ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ} FROM]์œผ๋กœ ๋Œ€์ƒ์„ ์„ ํƒํ•œ๋‹ค. ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์€ ์—”ํ‹ฐํ‹ฐ, ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…, ์Šค์นผ๋ผ ํƒ€์ž…์ด ์žˆ๋‹ค. ์Šค์นผ๋ผ ํƒ€์ž…์€ ์ˆซ์ž, ๋ฌธ์ž ๋“ฑ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋œปํ•œ๋‹ค.

์—”ํ‹ฐํ‹ฐ ํ”„๋กœ์ ์…˜

SELECT m FROM Member m      // ํšŒ์›
SELECT m.team FROM Member m // ํŒ€

์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ด€๋ฆฌํ•œ๋‹ค.

์ž„๋ฒ ๋””๋“œ ํƒ€์ž… ํ”„๋กœ์ ์…˜
์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์€ ์กฐํšŒ์˜ ์‹œ์ž‘์ ์ด ๋  ์ˆ˜ ์—†๋‹ค๋Š” ์ œ์•ฝ์ด ์žˆ๋‹ค. ์•„๋ž˜์— Address๋Š” ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์ด๋‹ค.

// ์ž˜๋ชป๋œ ์ฟผ๋ฆฌ
String query = "SELECT a From Address a";
// ์˜ฌ๋ฐ”๋ฅธ ์ฟผ๋ฆฌ
String query = "SELECT o.address From Order o";
List<Address> addresses = em.createQuery(query, Address.class).getResultList();

์‹คํ–‰๋œ ์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

select
    order.city,
    order.street,
    order.zipcode
from
    Orders order

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์€ ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…์ด ์•„๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ ‡๊ฒŒ ์ง์ ‘ ์กฐํšŒํ•œ ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ด€๋ฆฌ๋˜์ง€ ์•Š๋Š”๋‹ค.

์Šค์นผ๋ผ ํƒ€์ž… ํ”„๋กœ์ ์…˜
์ˆซ์ž, ๋ฌธ์ž, ๋‚ ์งœ์™€ ๊ฐ™์€ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ํƒ€์ž…๋“ค์€ ์Šค์นผ๋ผ ํƒ€์ž…์ด๋ผ ํ•œ๋‹ค.

String query = "SELECT username FROM Member m";
List<String> usernames = em.createQuery(query, String.class).getResultList();


// ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๋ ค๋ฉด DISTINCT๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
SELECT DISTINCT username From Member m

// ํ†ต๊ณ„
String query = "SELECT AVG(o.orderAmount) FROM Order o"
Double orderAmountAvg = em.createQuery(query, Double.class).getSingleResult();


์—ฌ๋Ÿฌ ๊ฐ’ ์กฐํšŒ
๊ผญ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋“ค๋งŒ ์„ ํƒํ•ด์„œ ์กฐํšŒํ•ด์•ผ ํ•  ๋•Œ๋„ ์žˆ๋‹ค. ์ด ๋•Œ๋Š” TypeQuery๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ  ๋Œ€์‹ ์— Query๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
List resultList = query.getResultList();

Iterator iterator = resultList.iterator();
while (iterator.hasNext()) {
    Object[] row = (Object[]) iterator.next();
    String username = (String) row[0];
    Integer age = (Integer) row[1];
}
Query query = em.createQuery("SELECT m.username, m.age FROM Member m");
List<Object[]> resultList = query.getResultList();

for (Object[] row : resultList) {
    String username = (String) row[0];
    Integer age = (Integer) row[1];
}

์Šค์นผ๋ผ ํƒ€์ž…๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…๋„ ์—ฌ๋Ÿฌ ๊ฐ’์„ ํ•จ๊ป˜ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

Query query = em.createQuery("SELECT o.member, o.product, o.orderAmount FROM Order o");
List<Object[]> resultList = query.getResultList();

for (Object[] row : resultList) {
    Member member = (Member) row[0];    // ์—”ํ‹ฐํ‹ฐ
    Product product = (Product) row[1]; // ์—”ํ‹ฐํ‹ฐ
    int orderAmount = (Integer) row[2]; // ์Šค์นผ๋ผ
}

๋ฌผ๋ก  ์ด๋•Œ๋„ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ด€๋ฆฌ๋œ๋‹ค.

NEW ๋ช…๋ น์–ด
ํ”„๋กœ์ ์…˜์„ UserDTO์ฒ˜๋Ÿผ ์˜๋ฏธ ์žˆ๋Š” ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

public class UserDTO {
    private String username;
    private int age;

    public UserDTO(String username, int age) {
        this.username = username;
        this.age = age;
    }
    ...
}
TypeQuery<UserDTO> query = em.createQuery("SELECT new jpabook.jpql.UserDTO(m.username, m.age) FROM Member m", UserDTO.class);

List<UserDTO> resultList = query.getResultList();

NEW ๋ช…๋ น์–ด ์‚ฌ์šฉ์‹œ ์ฃผ์˜ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ํŒจํ‚ค์ง€ ๋ช…์„ ํฌํ•จํ•œ ์ „์ฒด ํด๋ž˜์Šค ๋ช…์„ ์ž…๋ ฅํ•ด์•ผ ํ•œ๋‹ค.
  • ์ˆœ์„œ์™€ ํƒ€์ž…์ด ์ผ์น˜ํ•˜๋Š” ์ƒ์„ฑ์ž๊ฐ€ ํ•„์š”ํ•œ๋‹ค.


10.2.4 ํŽ˜์ด์ง• API

JPA ํŽ˜์ด์ง•

TypeQuery<Member> query = em.createQuery("SELECT m FROM Member m ORDER BY m.username DESC", Member.class);

query.setFirstResult(10); // ์กฐํšŒ ์‹œ์ž‘ ์œ„์น˜(0๋ถ€ํ„ฐ ์‹œ์ž‘)
query.setMaxResult(20);   // ์กฐํšŒํ•  ๋ฐ์ดํ„ฐ ์ˆ˜
query.getResultList();


HSQLDB ํŽ˜์ด์ง•

SELECT
    M.ID AS ID,
    M.AGE AS AGE,
    M.TEAM_ID AS TEAM_ID,
    M.NAME AS NAME
FROM
    MEMBER M
ORDER BY
    M.NAME DESC OFFSET ? LIMIT ?


MySQL ํŽ˜์ด์ง•

SELECT
    M.ID AS ID,
    M.AGE AS AGE,
    M.TEAM_ID AS TEAM_ID,
    M.NAME AS NAME
FROM
    MEMBER M
ORDER BY
    M.NAME DESC LIMIT ?, ?


PostgreSQL ํŽ˜์ด์ง•

SELECT
    M.ID AS ID,
    M.AGE AS AGE,
    M.TEAM_ID AS TEAM_ID,
    M.NAME AS NAME
FROM
    MEMBER M
ORDER BY
    M.NAME DESC LIMIT ? OFFSET ?


์˜ค๋ผํด ํŽ˜์ด์ง•

SELECT *
FROM
    (
        SELECT ROW_.*, ROWNUM ROWNUM_
            FROM
                (
                    SELECT
                        M.ID AS ID,
                        M.AGE AS AGE,
                        M.TEAM_ID AS TEAM_ID,
                        M.NAME AS NAME
                    FROM
                        MEMBER M
                    ORDER BY M.NAME
                ) ROW_
            WHERE ROWNUM <= ?
    )
WHERE ROWNUM_ > ?


SQLServer ํŽ˜์ด์ง•

WITH query AS (
    SELECT
    inner_query.*,
    ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as
        __hibernate_row_nr__
    FROM
        (
            select
                TOP(?) m.id as id,
                m.age as age,
                m.team_id as team_id,
                m.name as name
            from Member m
            order by m.name DESC
        ) inner_query
)
SELECT id, age, team_id, name
FROM query
WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?



10.2.5 ์ง‘ํ•ฉ๊ณผ ์ •๋ ฌ

์ง‘ํ•ฉ์€ ์ง‘ํ•ฉํ•จ์ˆ˜์™€ ํ•จ๊ป˜ ํ†ต๊ณ„ ์ •๋ณด๋ฅผ ๊ตฌํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

์ง‘ํ•ฉ ํ•จ์ˆ˜

ํ•จ์ˆ˜ ์„ค๋ช…
COUNT ๊ฒฐ๊ณผ ์ˆ˜๋ฅผ ๊ตฌํ•œ๋‹ค. ๋ฐ˜ํ™˜ ํƒ€์ž…: Long
MAX, MIN ์ตœ๋Œ€, ์ตœ์†Œ ๊ฐ’์„ ๊ตฌํ•œ๋‹ค. ๋ฌธ์ž, ์ˆซ์ž, ๋‚ ์งœ ๋“ฑ์— ์‚ฌ์šฉํ•œ๋‹ค.
AVG ํ‰๊ท ๊ฐ’์„ ๊ตฌํ•œ๋‹ค. ์ˆซ์žํƒ€์ž…๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜ํ™˜ ํƒ€์ž…: Double
SUM ํ•ฉ์„ ๊ตฌํ•œ๋‹ค. ์ˆซ์žํƒ€์ž…๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜ํ™˜ ํƒ€์ž…: ์ •์ˆ˜ํ•ฉ Long, ์†Œ์ˆ˜ํ•ฉ: Double, BigIntegerํ•ฉ: BigInteger, BigDecimalํ•ฉ: BigDecimal


์ง‘ํ•ฉ ํ•จ์ˆ˜ ์‚ฌ์šฉ ์‹œ ์ฐธ๊ณ ์‚ฌํ•ญ

  • NULL ๊ฐ’์€ ๋ฌด์‹œํ•˜๋ฏ€๋กœ ํ†ต๊ณ„์— ์žกํžˆ์ง€ ์•Š๋Š”๋‹ค. (DISTINCT๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์–ด๋„ ๋ฌด์‹œ๋œ๋‹ค.)
  • ๋งŒ์•ฝ ๊ฐ’์ด ์—†๋Š”๋ฐ SUM, AVG, MAX, MIN ํ•ฉ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด NULL ๊ฐ’์ด ๋œ๋‹ค. ๋‹จ COUNT๋Š” 0์ด ๋œ๋‹ค.
  • DISTINCT๋ฅผ ์ง‘ํ•ฉ ํ•จ์ˆ˜ ์•ˆ์— ์‚ฌ์šฉํ•ด์„œ ์ค‘๋ณต๋œ ๊ฐ’์„ ์ œ๊ฑฐํ•˜๊ณ  ๋‚˜์„œ ์ง‘ํ•ฉ์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ: select COUNT( DISTINCT m.age ) from Member m
  • DISTINCT๋ฅผ COUNT์—์„œ ์‚ฌ์šฉํ•  ๋•Œ ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์€ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.


GROUP BY, HAVING
GROUP BY๋Š” ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌํ•  ๋•Œ ํŠน์ • ๊ทธ๋ฃน๋ผ๋ฆฌ ๋ฌถ์–ด์ค€๋‹ค. HAVING์€ GROUP BY์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š”๋ฐ GROUP BY๋กœ ๊ทธ๋ฃนํ™”ํ•œ ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•„ํ„ฐ๋ง ํ•œ๋‹ค.

select
    t.name,
    COUNT(m.age),
    SUM(m.age),
    AVG(m.age),
    MAX(m.age),
    MIN(m.age)
from Member m LEFT JOIN m.team t
GROUP BY t.name
HAVING AVG(m.age) >= 10

๋ฌธ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

groupby_์ ˆ ::= GORUP BY {๋‹จ์ผ๊ฐ’ ๊ฒฝ๋กœ | ๋ณ„์นญ} +
having_์ ˆ ::= HAVING ์กฐ๊ฑด์‹


์ •๋ ฌ(ORDER BY)
๊ฒฐ๊ณผ๋ฅผ ์ •๋ ฌํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

select m from Member m order by m.age DESC, m.username ASC

๋ฌธ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

orderby_์ ˆ ::= ORDER BY {์ƒํƒœํ•„๋“œ ๊ฒฝ๋กœ | ๊ฒฐ๊ณผ ๋ณ€์ˆ˜ [ASC | DESC]}+
  • ASC: ์˜ค๋ฆ„์ฐจ์ˆœ(๊ธฐ๋ณธ๊ฐ’)
  • DESC: ๋‚ด๋ฆผ์ฐจ์ˆœ


10.2.6 JPQL ์กฐ์ธ

JPQL๋„ ์กฐ์ธ์„ ์ง€์›ํ•˜๋Š”๋ฐ SQL ์กฐ์ธ๊ณผ ๊ธฐ๋Šฅ์€ ๊ฐ™๊ณ  ๋ฌธ๋ฒ•๋งŒ ์•ฝ๊ฐ„ ๋‹ค๋ฅด๋‹ค.

๋‚ด๋ถ€ ์กฐ์ธ
๋‚ด๋ถ€์กฐ์ธ์€ INNER JOIN์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ INNER๋Š” ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

String teamName = "ํŒ€A";
String query = "SELECT m FROM Member m INNER JOIN m.team t WHERE t.name = :teamName";

List<Member> members = em.createQuery(query, Member.class)
                    .setParameter("teamName", teamName)
                    .getResultList();

์ฃผ์˜์‚ฌํ•ญ
JPQL ์กฐ์ธ์€ ์—ฐ๊ด€ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. SQL ์กฐ์ธ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋‹ค์Œ์€ ์ž˜๋ชป๋œ ์˜ˆ์ด๋‹ค.

-- ์ž˜๋ชป๋œ JPQL ์กฐ์ธ
FROM Member m JOIN Team t 


์™ธ๋ถ€ ์กฐ์ธ
JPQL์˜ ์™ธ๋ถ€ ์กฐ์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.

SELECT m
FROM Member m LEFT [OUTER] JOIN m.team t


์ปฌ๋ ‰์…˜ ์กฐ์ธ
์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋‚˜ ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„์ฒ˜๋Ÿผ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์—์„œ ์กฐ์ธํ•˜๋Š” ๊ฒƒ์„ ์ปฌ๋ ‰์…˜ ์กฐ์ธ์ด๋ผ ํ•œ๋‹ค.

  • [ํšŒ์› -> ํŒ€]์œผ๋กœ์˜ ์กฐ์ธ์€ ๋‹ค๋Œ€์ผ ์กฐ์ธ, ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ํ•„๋“œ(m.team) ์‚ฌ์šฉ
  • [ํŒ€ -> ํšŒ์›]์œผ๋กœ์˜ ์กฐ์ธ์€ ์ผ๋Œ€๋‹ค ์กฐ์ธ, ์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ํ•„๋“œ(m.members) ์‚ฌ์šฉ
-- ํŒ€๊ณผ ํšŒ์›๋ชฉ๋ก์„ ์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ๋กœ ์™ธ๋ถ€์กฐ์ธ ํ–ˆ๋‹ค.
SELECT t, m FROM Team t LEFT JOIN t.members m


์„ธํƒ€ ์กฐ์ธ
WHERE ์ ˆ์„ ์‚ฌ์šฉํ•ด์„œ ์„ธํƒ€ ์กฐ์ธ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ธํƒ€ ์กฐ์ธ์€ ๋‚ด๋ถ€ ์กฐ์ธ๋งŒ ์ง€์›ํ•œ๋‹ค. ์„ธํƒ€ ์กฐ์ธ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ „ํ˜€ ๊ด€๊ณ„์—†๋Š” ์—”ํ‹ฐํ‹ฐ๋„ ์กฐ์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

-- JPQL
select count(m) from Member m, Team t
where m.username = t.team

-- sql
select count(m.id)
from
    member m cross join team t
where
    m.username = t.name


JOIN ON ์ ˆ(JPA 2.1)
JPA 2.1๋ถ€ํ„ฐ ์กฐ์ธํ•  ๋•Œ ON ์ ˆ์„ ์ง€์›ํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ ๋‚ด๋ถ€ ์กฐ์ธ์ด ON ์ ˆ์€ WHERE ์ ˆ์„ ์‚ฌ์šฉํ•  ๋•Œ์™€ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ ๋ณดํ†ต ON ์ ˆ์€ ์™ธ๋ถ€ ์กฐ์ธ์—์„œ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค.

-- JPQL
select m, t
from Member m
    left join m.team t on t.name = 'A'

-- SQL
select m.*, t.*
from Member m
    left join Team t on m.team_id = t.id 
    and t.name = 'A'



10.2.7 ํŽ˜์น˜ ์กฐ์ธ

  • ํŽ˜์น˜(fetch) ์กฐ์ธ์€ JPQL์—์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
  • ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋‚˜ ์ปฌ๋ ‰์…˜์„ ํ•œ ๋ฒˆ์— ๊ฐ™์ด ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
  • join fetch ๋ช…๋ น์–ด๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌธ๋ฒ•

ํŽ˜์น˜ ์กฐ์ธ ::= [ LEFT [OUTER] | INNER ] JOIN FETCH ์กฐ์ธ๊ฒฝ๋กœ


์—”ํ‹ฐํ‹ฐ ํŽ˜์น˜ ์กฐ์ธ
๋‹ค์Œ์€ ํŽ˜์น˜ ์กฐ์ธ์„ ์‚ฌ์šฉํ•ด์„œ ํšŒ์› ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๋ฉด์„œ ์—ฐ๊ด€๋œ ํŒ€ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์กฐํšŒ ํ•˜๋Š” JPQL

-- ํŽ˜์น˜ ์กฐ์ธ JPQL
select m
from Member m join fetch m.team


-- ์‹คํ–‰๋œ SQL
SELECT
    M.*, T.*
FROM MEMBER T
INNER JOIN TEAM T ON M.TEAM_ID = T.ID

JPQL์กฐ์ธ๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ m.team ๋‹ค์Œ์— ๋ณ„์นญ์ด ์—†๋Š”๋ฐ ํŽ˜์น˜ ์กฐ์ธ์€ ๋ณ„์นญ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. (ํ•˜์ง€๋งŒ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ํŽ˜์น˜ ์กฐ์ธ์—๋„ ๋ณ„์นญ์„ ํ—ˆ์šฉํ•œ๋‹ค.)

์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ
์ผ๋Œ€๋‹ค ๊ด€๊ณ„์ธ ์ปฌ๋ ‰์…˜์„ ํŽ˜์น˜ ์กฐ์ธ

-- ์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ JPQL
select t
from Team t join fetch t.members
where t.name = 'ํŒ€A'


-- ์‹คํ–‰๋œ SQL
SELECT
    T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID = M.TEAM_ID
WHERE T.NAME = 'ํŒ€A'

๋ฌธ์ œ๋Š” TEAM ํ…Œ์ด๋ธ”์—์„œ ํŒ€A๋Š” ํ•˜๋‚˜์ง€๋งŒ MEMBERํ…Œ์ด๋ธ”๊ณผ ์กฐ์ธํ•˜๋ฉด์„œ ๊ฒฐ๊ณผ๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค. ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํŒ€A, ํšŒ์›1, ํšŒ์›2 ์žˆ๋‹ค๊ณ  ํ•˜๋ฉด ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

teamname = ํŒ€A, team = Team@0x100
    username = ํšŒ์›1, member = Member@0x200
    username = ํšŒ์›2, member = Member@0x300
teamname = ํŒ€A, team = Team@0x100
    username = ํšŒ์›1, member = Member@0x200
    username = ํšŒ์›2, member = Member@0x300


ํŽ˜์น˜ ์กฐ์ธ๊ณผ DISTINCT
์œ„์˜ ์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ์€ ํŒ€A๊ฐ€ ์ค‘๋ณต์œผ๋กœ ์กฐํšŒ๋œ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด DISTINCT๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

select distinct t
from Team t join fetch t.members
where t.name = 'ํŒ€A'

์œ„ ์ƒํ™ฉ์˜ ๋ฐ์ดํ„ฐ๋Š” SQL์—์„œ DISTINCT๋ฅผ ํ•ด๋„ ํšจ๊ณผ๊ฐ€ ์—†๋‹ค.

๋กœ์šฐ ๋ฒˆํ˜ธ ํŒ€ ํšŒ์›
1 ํŒ€A ํšŒ์›1
2 ํŒ€A ํšŒ์›2

ํ•˜์ง€๋งŒ JPQL์˜ select distinct t์˜ ์˜๋ฏธ๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ํŒ€A๋Š” ํ•˜๋‚˜๋งŒ ์กฐํšŒ๋œ๋‹ค.

teamname = ํŒ€A, team = Team@0x100
    username = ํšŒ์›1, member = Member@0x200
    username = ํšŒ์›2, member = Member@0x300


ํŽ˜์น˜ ์กฐ์ธ๊ณผ ์ผ๋ฐ˜ ์กฐ์ธ์˜ ์ฐจ์ด

  • ์ผ๋ฐ˜ ์กฐ์ธ: JPQL์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ ์—ฐ๊ด€๊ด€๊ณ„๊นŒ์ง€ ๊ณ ๋ คํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‹จ์ง€ SELECT ์ ˆ์— ์ง€์ •ํ•œ ์—”ํ‹ฐํ‹ฐ๋งŒ ์กฐํšŒํ•  ๋ฟ์ด๋‹ค.
  • ํŽ˜์น˜ ์กฐ์ธ: SELECT ์ ˆ์— ์ง€์ •ํ•œ ์—”ํ‹ฐํ‹ฐ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ง€์ •ํ•˜์ง€ ์•Š์•˜์–ด๋„ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์กฐํšŒํ•œ๋‹ค.


ํŽ˜์น˜ ์กฐ์ธ์˜ ํŠน์ง•๊ณผ ํ•œ๊ณ„

  • SQL ํ•œ๋ฒˆ์œผ๋กœ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋“ค์„ ์กฐํšŒํ•˜์—ฌ SQL ํ˜ธ์ถœ ํšŸ์ˆ˜๋ฅผ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • JPQL์—์„œ ํŽ˜์น˜ ์กฐ์ธ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ธ€๋กœ๋ฒŒ ์ „๋žต๋ณด๋‹ค ์šฐ์„  ์ ์šฉ๋œ๋‹ค.
    • ๊ธ€๋กœ๋ฒŒ ์ „๋žต ์˜ˆ: @OneToMany(fetch = FetchType.LAZY)
  • ํŽ˜์น˜ ์กฐ์ธ์„ ํ•˜๋ฉด ์ฟผ๋ฆฌ ์‹œ์ ์— ์กฐํšŒํ•˜๋ฏ€๋กœ ์ค€์˜์† ์ƒํƒœ์—์„œ๋„ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํŽ˜์น˜ ์กฐ์ธ ๋Œ€์ƒ์—๋Š” ๋ณ„์นญ์„ ์ค„ ์ˆ˜ ์—†๋‹ค.
    • ๋”ฐ๋ผ์„œ SELECT, WHERE, ์„œ๋น„ ์ฟผ๋ฆฌ์— ํŽ˜์น˜ ์กฐ์ธ ๋Œ€์ƒ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ๋Š” ํŽ˜์น˜ ์กฐ์ธ์— ๋ณ„์นญ์„ ์ง€์›ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ์—ฐ๊ด€๋œ ๋ฐ์ดํ„ฐ ์ˆ˜๊ฐ€ ๋‹ฌ๋ผ์ ธ ๋ฌด๊ฒฐ์„ฑ์ด ๊นจ์งˆ ์ˆ˜ ์žˆ๋‹ค.
  • ๋‘˜ ์ด์ƒ์˜ ์ปฌ๋ ‰์…˜์„ ํŽ˜์น˜ํ•  ์ˆ˜ ์—†๋‹ค.
  • ์ปฌ๋ ‰์…˜์„ ํŽ˜์น˜ ์กฐ์ธํ•˜๋ฉด ํŽ˜์ด์ง• API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
    • ์ปฌ๋ ‰์…˜(์ผ๋Œ€๋‹ค)์ด ์•„๋‹Œ ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ(์ผ๋Œ€์ผ, ๋‹ค๋Œ€์ผ)์€ ํŽ˜์น˜ ์กฐ์ธ ๊ฐ€๋Šฅ
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ์ปฌ๋ ‰์…˜์„ ํŽ˜์น˜ ์กฐ์ธํ•˜๊ณ  ํŽ˜์ด์ง• API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒฝ๊ณ  ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์ถ”์ฒœ์€ ๊ธ€๋กœ๋ฒŒ ์ „๋žต์€ ์ง€์—ฐ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜๊ณ  ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•˜๋ฉด ๊ทธ ๋•Œ ํŽ˜์น˜ ์กฐ์ธ์„ ์ ์šฉํ•œ๋‹ค.



10.2.8 ๊ฒฝ๋กœ ํ‘œํ˜„์‹

๊ฒฝ๋กœ ํ‘œํ˜„์‹์ด๋ž€ m.username, m.team ์ฒ˜๋Ÿผ .(์ )์„ ์ฐ์–ด ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ฒฝ๋กœ ํ‘œํ˜„์‹์˜ ์šฉ์–ด ์ •๋ฆฌ

  • ์ƒํƒœ ํ•„๋“œ: ๋‹จ์ˆœํžˆ ๊ฐ’์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ•„๋“œ
  • ์—ฐ๊ด€ ํ•„๋“œ: ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์œ„ํ•œ ํ•„๋“œ, ์ž„๋ฒ ๋””๋“œ ํƒ€์ž… ํฌํ•จ
    • ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ํ•„๋“œ: @ManyToOne, @OneToOne, ๋Œ€์ƒ์ด ์—”ํ‹ฐํ‹ฐ
    • ์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ: @OneToMany, @ManyToMany, ๋Œ€์ƒ์ด ์ปฌ๋ ‰์…˜
@Entity
public class Member {
    ...์ƒ๋žต...
    @Column(name = "name")
    private String username;    // ์ƒํƒœ ํ•„๋“œ

    @ManyToOne(...)
    private Team team;          // ์—ฐ๊ด€ ํ•„๋“œ (๋‹จ์ผ ๊ฐ’)

    @OneToMany(...)
    private List<Order> orders; // ์—ฐ๊ด€ ํ•„๋“œ (์ปฌ๋ ‰์…˜ ๊ฐ’)
    ...์ƒ๋žต...
}


๊ฒฝ๋กœ ํ‘œํ˜„์‹ ํŠน์ง•
JPQL์—์„œ ๊ฒฝ๋กœ ํ‘œํ˜„์‹์˜ ํŠน์ง•

  • ์ƒํƒœ ํ•„๋“œ ๊ฒฝ๋กœ: ๊ฒฝ๋กœ ํƒ์ƒ‰์˜ ๋์ด๋‹ค. ๋”๋Š” ํƒ์ƒ‰ํ•  ์ˆ˜ ์—†๋‹ค.

      -- JPQL
      select m.username, m.age from Member m
    
      -- SQL ๋ณ€ํ™˜
      select m.name, m.age from Member m
    


  • ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ: ๋ฌต์‹œ์ ์œผ๋กœ ๋‚ด๋ถ€ ์กฐ์ธ์ด ์ผ์–ด๋‚œ๋‹ค. ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ๋Š” ๊ณ„์† ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

      -- JPQL
      select o.member from Order o  -- ๋ฌต์‹œ์  ์กฐ์ธ
      -- where o.product.name = 'productA' ์ด๋ ‡๊ฒŒ ๊ณ„์† ํƒ์ƒ‰ ๊ฐ€๋Šฅ
    
      -- SQL ๋ณ€ํ™˜
      select m.*
      from Order o
          inner join Member m on o.member_id = m.id -- ๋ฌต์‹œ์ ์œผ๋กœ ๋‚ด๋ถ€ ์กฐ์ธ์ด ์ผ์–ด๋‚ฌ๋‹ค
    


  • ์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ: ๋ฌต์‹œ์ ์œผ๋กœ ๋‚ด๋ถ€ ์กฐ์ธ์ด ์ผ์–ด๋‚œ๋‹ค. ๋”๋Š” ํƒ์ƒ‰ํ•  ์ˆ˜ ์—†๋‹ค. ๋‹จ FROM ์ ˆ์—์„œ ์กฐ์ธ์„ ํ†ตํ•ด ๋ณ„์นญ์„ ์–ป์œผ๋ฉด ๋ณ„์นญ์œผ๋กœ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

      -- JPQL
      -- ์„ฑ๊ณต
      select t.members from Team t 
    
      -- ์‹คํŒจ, ์ปฌ๋ ‰์…˜ ๊ฐ’์€ ํƒ์ƒ‰์ด ๋ถˆ๊ฐ€ 
      select t.members.username from Team t 
      -- ๋‹ค์Œ์ฒ˜๋Ÿผ ๋ณ„์นญ์„ ์–ป์–ด์„œ ๊ฒฝ๋กœํƒ์ƒ‰์„ ํ•ด์•ผ ํ•œ๋‹ค.
      select m.username from Team t join t.members m
    
      -- ์ปฌ๋ ‰์…˜์˜ ํฌ๊ธฐ๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋Š” size๊ธฐ๋Šฅ ์ œ๊ณต, ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด countํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” SQL๋กœ ๋ณ€ํ™˜๋œ๋‹ค.
      select t.members.size from Team t 
    



๊ฒฝ๋กœ ํƒ์ƒ‰์„ ์‚ฌ์šฉํ•œ ๋ฌต์‹œ์  ์กฐ์ธ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

  • ํ•ญ์ƒ ๋‚ด๋ถ€ ์กฐ์ธ์ด๋‹ค.
  • ์ปฌ๋ ‰์…˜์€ ๊ฒฝ๋กœ ํƒ์ƒ‰์˜ ๋์ด๋‹ค. ์ปฌ๋ ‰์…˜์—์„œ ๊ฒฝ๋กœ ํƒ์ƒ‰์„ ํ•˜๋ ค๋ฉด ๋ช…์‹œ์ ์œผ๋กœ ์กฐ์ธํ•ด์„œ ๋ณ„์นญ์„ ์–ป์–ด์•ผ ํ•œ๋‹ค.
  • ๊ฒฝ๋กœ ํƒ์ƒ‰์€ ์ฃผ๋กœ SELECT, WHERE ์ ˆ(๋‹ค๋ฅธ ๊ณณ์—์„œ๋„ ์‚ฌ์šฉ๋จ)์—์„œ ์‚ฌ์šฉํ•˜์ง€๋งŒ ๋ฌต์‹œ์  ์กฐ์ธ์œผ๋กœ ์ธํ•ด SQL์˜ FROM์ ˆ์— ์˜ํ–ฅ์„ ์ค€๋‹ค.

๋‹จ์ˆœํ•˜๊ณ  ์„ฑ๋Šฅ์— ์ด์Šˆ๊ฐ€ ์—†์œผ๋ฉด ๋ฌธ์ œ๊ฐ€ ์•ˆ ๋˜์ง€๋งŒ, ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•˜๋ฉด ๋ถ„์„ํ•˜๊ธฐ ์‰ฝ๋„๋ก ๋ฌต์‹œ์  ์กฐ์ธ๋ณด๋‹ค๋Š” ๋ช…์‹œ์  ์กฐ์ธ์„ ์‚ฌ์šฉํ•˜์ž.



10.2.9 ์„œ๋ธŒ ์ฟผ๋ฆฌ

JPQL๋„ SQL์ฒ˜๋Ÿผ ์„œ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์ง€์›ํ•œ๋‹ค.

  • ์‚ฌ์šฉ๊ฐ€๋Šฅ: WHERE, HAVING ์ ˆ
  • ์‚ฌ์šฉ๋ถˆ๊ฐ€: SELECT, FROM ์ ˆ
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์˜ HQL์€ SELECT ์ ˆ์˜ ์„œ๋ธŒ ์ฟผ๋ฆฌ๋„ ํ—ˆ์šฉํ•œ๋‹ค. ํ•˜์ง€๋งŒ FROM ์ ˆ์˜ ์„œ๋ธŒ ์ฟผ๋ฆฌ๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์„œ๋ธŒ์ฟผ๋ฆฌ ์˜ˆ

-- ๋‚˜์ด๊ฐ€ ํ‰๊ท ๋ณด๋‹ค ๋งŽ์€ ํšŒ์›
select m from Member m
where m.age > (select avg(m2.age) from Member m2)

-- ํ•œ ๊ฑด์ด๋ผ๋„ ์ฃผ๋ฌธํ•œ ๊ณ ๊ฐ
select m from Member m
where (select count(o) from Order o where m = o.member) > 0

-- ํ•œ ๊ฑด์ด๋ผ๋„ ์ฃผ๋ฌธํ•œ ๊ณ ๊ฐ
-- size ์ด์šฉ, ์œ„์™€ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™๋‹ค.(์‹คํ–‰ SQL๋„ ๊ฐ™๋‹ค)
select m from Member m
where m.orders.size > 0


์„œ๋ธŒ ์ฟผ๋ฆฌ ํ•จ์ˆ˜
์„œ๋ธŒ ์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ ํ•จ์ˆ˜๋“ค๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • EXISTS
    • ๋ฌธ๋ฒ•: [NOT] EXISTS (subquery)
    • ์„ค๋ช…: ์„œ๋ธŒ์ฟผ๋ฆฌ์— ๊ฒฐ๊ณผ๊ฐ€ ์กด์žฌํ•˜๋ฉด ์ฐธ์ด๋‹ค.
      select m from Member m
      where exists (select t from m.team t where t.name = 'ํŒ€A')
      


  • {ALL | ANY | SOME}
    • ๋ฌธ๋ฒ•: {ALL | ANY | SOME} (subquery)
    • ์„ค๋ช…: ๋น„๊ต ์—ฐ์‚ฐ์ž์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค. {= | > | >= | < | <= | <>}
      • ALL: ์กฐ๊ฑด์„ ๋ชจ๋‘ ๋งŒ์กฑํ•˜๋ฉด ์ฐธ์ด๋‹ค.
      • ANY ํ˜น์€ SOME: ๋‘˜์€ ๊ฐ™์€ ์˜๋ฏธ๋‹ค. ์กฐ๊ฑด์„ ํ•˜๋‚˜๋ผ๋„ ๋งŒ์กฑํ•˜๋ฉด ์ฐธ์ด๋‹ค.
      select o from Order o
      where o.orderAmount > ALL (select p.stockAmount from Product p)
      
      select m from Member m
      where m.team = ANY (select t from Team t)
      


  • IN
    • ๋ฌธ๋ฒ•: [NOT] IN (subquery)
    • ์„ค๋ช…: ์„œ๋ธŒ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๊ฐ™์€ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ์ฐธ์ด๋‹ค. ์ฐธ๊ณ ๋กœ IN์€ ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ ์•„๋‹Œ๊ณณ์—์„œ๋„ ์‚ฌ์šฉํ•œ๋‹ค.
      select t from Team t
      where t IN (select t2 From Team t2 JOIN t2.members m2 where m2.age >= 20)
      



10.2.10 ์กฐ๊ฑด์‹

ํƒ€์ž… ํ‘œํ˜„
JPQL์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํƒ€์ž…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œํ•œ๋‹ค. ๋Œ€์†Œ๋ฌธ์ž๋Š” ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ข…๋ฅ˜ ์„ค๋ช… ์˜ˆ์ œ
๋ฌธ์ž ์ž‘์€ ๋”ฐ์˜ดํ‘œ ์‚ฌ์ด์— ํ‘œํ˜„
์ž‘์€ ๋”ฐ์˜ดํ‘œ๋ฅผ ํ‘œํ˜„ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์ž‘์€ ๋”ฐ์˜ดํ‘œ ์—ฐ์† ๋‘๊ฐœ('') ์‚ฌ์šฉ
'HELLO'
'She''s'
์ˆซ์ž L(Long ํƒ€์ž… ์ง€์ •)
D(Doublc ํƒ€์ž… ์ง€์ •)
F(Float ํƒ€์ž… ์ง€์ •)
10L
10D
10F
๋‚ ์งœ DATE {d 'yyyy-mm-dd'}
TIME {t 'hh-mm-ss'}
DATETIME {ts 'yyyy-mm-dd hh-mm:ss.f'}
{d '2012-03-24'}
{t '10-11-28'}
{ts '2012-03-24 10-11-28.123'}
m.createDate = {d '2012-03-24'}
Boolen TRUE, FALSE
Enum ํŒจํ‚ค์ง•๋ช…์„ ํฌํ•จํ•œ ์ „์ฒด ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. jpabook.MemberType.Admin
์—”ํ‹ฐํ‹ฐ ํƒ€์ž… ์—”ํ‹ฐํ‹ฐ์˜ ํƒ€์ž…์„ ํ‘œํ˜„ํ•œ๋‹ค. ์ฃผ๋กœ ์ƒ์†๊ณผ ๊ด€๋ จํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค. TYPE(m) = Member


์—ฐ์‚ฐ์ž ์šฐ์„  ์ˆœ์œ„

  1. ๊ฒฝ๋กœ ํƒ์ƒ‰ ์—ฐ์‚ฐ (.)
  2. ์ˆ˜ํ•™ ์—ฐ์‚ฐ: +, -(๋‹จํ•ญ ์—ฐ์‚ฐ์ž), *, /, +, -
  3. ๋น„๊ต์—ฐ์‚ฐ: =, >, >=, <, <=, <>
  4. ๋…ผ๋ฆฌ ์—ฐ์‚ฐ: NOT, AND, OR


๋…ผ๋ฆฌ ์—ฐ์‚ฐ๊ณผ ๋น„๊ต์‹

  • ๋…ผ๋ฆฌ ์—ฐ์‚ฐ
    • AND
    • OR
    • NOT
  • ๋น„๊ต์‹
    • =, >, >=, <, <=, <>


Between, IN, Like, NULL ๋น„๊ต

  • Between ์‹
    • ๋ฌธ๋ฒ•: x [NOT] BETWEEN A AND B
    • ์„ค๋ช…: x๋Š” A ~ B ์‚ฌ์ด์˜ ๊ฐ’์ด๋ฉด ์ฐธ์ด๋‹ค.(A, B๊ฐ’ ํฌํ•จ)
  • IN ์‹
    • ๋ฌธ๋ฒ•: x [NOT] IN
    • ์„ค๋ช…: X์™€ ๊ฐ™์€ ๊ฐ’์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ์ฐธ์ด๋‹ค.
  • Like ์‹
    • ๋ฌธ๋ฒ•: ๋ฌธ์žํ‘œํ˜„์‹ [NOT] LIKE ํŒจํ„ด๊ฐ’ [ESCAPE ์ด์Šค์ผ€์ดํ”„๋ฌธ์ž]
    • ์„ค๋ช…: ๋ฌธ์žํ‘œํ˜„์‹๊ณผ ํŒจํ…€๊ฐ’์„ ๋น„๊ตํ•œ๋‹ค.
      • %(ํผ์„ผํŠธ): ์•„๋ฌด ๊ฐ’๋“ค์ด ์ž…๋ ฅ๋˜์–ด๋„ ๋œ๋‹ค. ๊ฐ’์ด ์—†์–ด๋„ ๋œ๋‹ค.
      • _(์–ธ๋”๋ผ์ธ): ํ•œ ๊ธ€์ž๋Š” ์•„๋ฌด ๊ฐ’์ด ์ž…๋ ฅ๋˜์–ด๋„ ๋˜์ง€๋งŒ ๊ฐ’์ด ์žˆ์–ด์•ผ ๋œ๋‹ค.
    -- ํšŒ์›A, ํšŒ์›1..
    where m.username like 'ํšŒ์›_'
    
    -- ํšŒ์›3
    where m.username like '__3'
    
    -- ํšŒ์›%
    where m.username like 'ํšŒ์›\%' ESCAPE '\'
    
  • NULL ๋น„๊ต์‹
    • ๋ฌธ๋ฒ•: {๋‹จ์ผ๊ฐ’ ๊ฒฝ๋กœ | ์ž…๋ ฅ ํŒŒ๋ผ๋ฏธํ„ฐ} IS [NOT] NULL
    • ์„ค๋ช…: NULL์ธ์ง€ ๋น„๊ตํ•œ๋‹ค. NULL์€ = ์œผ๋กœ ๋น„๊ตํ•˜๋ฉด ์•ˆ ๋˜๊ณ  ๊ผญ IS NULL์„ ์‚ฌ์šฉ ํ•ด์•ผ ํ•œ๋‹ค.
     where m.username is null
     where null = null -- ๊ฑฐ์ง“
     where 1 = 1 -- ์ฐธ
    



์ปฌ๋ ‰์…˜ ์‹
์ปฌ๋ ‰์…˜์—๋งŒ ์‚ฌ์šฉํ•˜๋Š” ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์ด๋‹ค. ์ปฌ๋ ‰์…˜์€ ์ปฌ๋ ‰์…˜ ์‹ ์ด์™ธ์— ๋‹ค๋ฅธ ์‹์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

  • ๋นˆ ์ปฌ๋ ‰์…˜ ๋น„๊ต ์‹
    • ๋ฌธ๋ฒ•: {์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ} IS [NOT] EMPTY
    • ์„ค๋ช…: ์ปฌ๋ ‰์…˜์— ๊ฐ’์ด ๋น„์—ˆ์œผ๋ฉด ์ฐธ
    -- JPQL
    select m from Member m
    where m.orders is not empty
    
    -- SQL ๋ณ€ํ™˜
    select m.* from Member m
    where exists (
      select o.id
      from Orders o
      where m.id = o.member_id
    )
    
  • ์ปฌ๋ ‰์…˜์˜ ๋ฉค๋ฒ„ ์‹
    • ๋ฌธ๋ฒ•: {์—”ํ‹ฐํ‹ฐ๋‚˜ ๊ฐ’} [NOT] MEMBER [OF] {์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ}
    • ์„ค๋ช…: ์—”ํ‹ฐํ‹ฐ๋งˆ ๊ฐ’์ด ์ปฌ๋ ‰์…˜์— ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด ์ฐธ
    select t from Team t
    where :memberParam member of t.members
    



์Šค์นผ๋ผ ์‹
์Šค์นผ๋ผ๋Š” ์ˆซ์ž, ๋ฌธ์ž, ๋‚ ์งœ, case, ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…๊ฐ™์€ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํƒ€์ž…๋“ค์„ ๋งํ•œ๋‹ค.

  • ์ˆ˜ํ•™ ์‹
    • +, -: ๋‹จํ•ญ ์—ฐ์‚ฐ์ž (์–‘์ˆ˜, ์Œ์ˆ˜)
    • *, /, +, -: ์‚ฌ์น™ ์—ฐ์‚ฐ

  • ๋ฌธ์ž ํ•จ์ˆ˜
ํ•จ์ˆ˜ ์„ค๋ช… ์˜ˆ์ œ
CONCAT(๋ฌธ์ž1, ๋ฌธ์ž2, ...) ๋ฌธ์ž๋ฅผ ํ•ฉํ•œ๋‹ค. CONCAT('A', 'B') = AB
SUBSTRING(๋ฌธ์ž, ์œ„์น˜, [๊ธธ์ด]) ์œ„์น˜๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด ๊ธธ์ด๋งŒํผ ๋ฌธ์ž๋ฅผ ๊ตฌํ•œ๋‹ค. ๊ธธ์ด ๊ฐ’์ด ์—†์œผ๋ฉด ๋‚˜๋จธ์ง€ ์ „์ฒด ๊ธธ์ด๋ฅผ ๋œปํ•œ๋‹ค. SUBSTRING('ABCDEF', 2, 3) = BCD
TRIM([[LEADING | TRAILING | BOTH] [ํŠธ๋ฆผ๋ฌธ์ž] FROM] ๋ฌธ์ž) LEADING: ์™ผ์ชฝ๋งŒ ํŠธ๋ฆผ
TRAILING: ์˜ค๋ฅธ์ชฝ๋งŒ ํŠธ๋ฆผ
BOTH: ์–‘์ชฝ ๋‹ค ํŠธ๋ฆผ
๊ธฐ๋ณธ๊ฐ’์€ BOTH
ํŠธ๋ฆผ ๋ฌธ์ž์˜ ๊ธฐ๋ณธ๊ฐ’์€ ๊ณต๋ฐฑ(space)์ด๋‹ค.
TRIM('ABC') = 'ABC'
LOWER(๋ฌธ์ž) ์†Œ๋ฌธ์ž๋กœ ๋ณ€๊ฒฝ LOWER('ABC') = 'abc'
UPPER(๋ฌธ์ž) ๋Œ€๋ฌธ์ž๋กœ ๋ณ€๊ฒฝ UPPER('abc') = 'ABC'
LENGTH(๋ฌธ์ž) ๋ฌธ์ž ๊ธธ์ด LENGTH('ABC') = 3
LOCATE(์ฐพ์„ ๋ฌธ์ž, ์›๋ณธ ๋ฌธ์ž, [๊ฒ€์ƒ‰ ์‹œ์ž‘ ์œ„์น˜]) ๊ฒ€์ƒ‰์œ„์น˜๋ถ€ํ„ฐ ๋ฌธ์ž๋ฅผ ๊ฒ€์ƒ‰ํ•œ๋‹ค. 1๋ถ€ํ„ฐ ์‹œ์ž‘, ๋ชป ์ฐพ์œผ๋ฉด 0 ๋ฐ˜ํ™˜ LOCATE('DE', 'ABCDEFG') = 4


  • ์ˆ˜ํ•™ ํ•จ์ˆ˜
ํ•จ์ˆ˜ ์„ค๋ช… ์˜ˆ์ œ
ABS(์ˆ˜ํ•™์‹) ์ ˆ๋Œ€๊ฐ’์„ ๊ตฌํ•œ๋‹ค ABS(-10) = 10
SQRT(์ˆ˜ํ•™์‹) ์ œ๊ณฑ๊ทผ์„ ๊ตฌํ•œ๋‹ค. SQRT(4) = 2.0
MOD(์ˆ˜ํ•™์‹, ๋‚˜๋ˆŒ ์ˆ˜) ๋‚˜๋จธ์ง€๋ฅผ ๊ตฌํ•œ๋‹ค. MOD(4, 3) = 1
SIZE(์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ์‹) ์ปฌ๋ ‰์…˜์˜ ํฌ๊ธฐ๋ฅผ ๊ตฌํ•œ๋‹ค SIZE(t.members)
INDEX(๋ณ„์นญ) LIST ํƒ€์ž… ์ปฌ๋ ‰์…˜์˜ ์œ„์น˜๊ฐ’์„ ๊ตฌํ•จ, ๋‹จ ์ปฌ๋ ‰์…˜์ด @OrderColumn์„ ์‚ฌ์šฉํ•˜๋Š” LIST ํƒ€์ž…์ผ ๋•Œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. t.members m where INDEX(m) > 0


  • ๋‚ ์งœ ํ•จ์ˆ˜
    ๋‚ ์งœ ํ•จ์ˆ˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ˜„์žฌ ์‹œ๊ฐ„์„ ์กฐํšŒํ•œ๋‹ค.
    • CURRENT_DATE: ํ˜„์žฌ ๋‚ ์งœ
    • CURRENT_TIME: ํ˜„์žฌ ์‹œ๊ฐ„
    • CURRENT_TIMESTAMP: ํ˜„์žฌ ๋‚ ์งœ ์‹œ๊ฐ„
select CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP from Team t

select e from Event e where e.endDate < CURRENT_DATE

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ๋‚ ์งœ ํƒ€์ž…์—์„œ ๋…„, ์›”, ์ผ, ์‹œ๊ฐ„, ๋ถ„, ์ดˆ ๊ฐ’์„ ๊ตฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•œ๋‹ค.

-- YEAR, MONTH, DAY, HOUR, MINUTE, SECOND
select year(CURRENT_TIMESTAMP), month(CURRENT_TIMESTAMP) from Member


CASE ์‹
ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ถ„๊ธฐํ•  ๋•Œ CASE ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ๊ธฐ๋ณธ CASE
    • ๋ฌธ๋ฒ•:
      CASE
          {WHEN <์กฐ๊ฑด์‹> THEN <์Šค์นผ๋ผ์‹>}+
          ELSE <์Šค์นผ๋ผ์‹>
      END
      
      select
          case when m.age <= 10 then 'ํ•™์ƒ์š”๊ธˆ'
               when m.age >= 10 then '๊ฒฝ๋กœ์š”๊ธˆ'
               else '์ผ๋ฐ˜์š”๊ธˆ'
          end
      from Member m
      


  • ์‹ฌํ”Œ CASE
    • ๋ฌธ๋ฒ•:
          CASE
              {WHEN <์Šค์นผ๋ผ์‹1> THEN <์Šค์นผ๋ผ์‹2>}+
              ELSE <์Šค์นผ๋ผ์‹>
          END
      
      select
          case t.name
              when 'ํŒ€A' then '์ธ์„ผํ‹ฐ๋ธŒ110%'
              when 'ํŒ€B' then '์ธ์„ผํ‹ฐ๋ธŒ120%'
              else '์ธ์„ผํ‹ฐ๋ธŒ105%'
          end
      from Team t
      


  • COALESCE
    • ๋ฌธ๋ฒ•: COALESCE(<์Šค์นผ๋ผ์‹> {, <์Šค์นผ๋ผ์‹>}+)
    • ์„ค๋ช…: ์Šค์นผ๋ผ์‹์„ ์ฐจ๋ก€๋Œ€๋กœ ์กฐํšŒํ•ด์„œ null์ด ์•„๋‹ˆ๋ฉด ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      -- m.username์ด null์ด ์•„๋‹ˆ๋ฉด '์ด๋ฆ„ ์—†๋Š” ํšŒ์›'์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      select coalesce(m.username, '์ด๋ฆ„ ์—†๋Š” ํšŒ์›') from Member m
      


  • NULLIF
    • ๋ฌธ๋ฒ•: NULLIF(<์Šค์นผ๋ผ์‹>, <์Šค์นผ๋ผ์‹>)
    • ์„ค๋ช…: ๋‘ ๊ฐ’์ด ๊ฐ™์œผ๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๋‹ค๋ฅด๋ฉด ์ฒซ ๋ฒˆ์งธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      -- ๊ด€๋ฆฌ์ž๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋ณธ์ธ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜
      select nullif(m.username, '๊ด€๋ฆฌ์ž') from Member m
      




10.2.11 ๋‹คํ˜•์„ฑ ์ฟผ๋ฆฌ

JPQL๋กœ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๋ฉด ๊ทธ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์กฐํšŒ๋œ๋‹ค.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {...}
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
    private String author;
    ...
}

๋‹ค์Œ์„ ์กฐํšŒํ•˜๋ฉด Item์˜ ์ž์‹๋„ ํ•จ๊ป˜ ์กฐํšŒ๋œ๋‹ค.

List resultList = em.createQuery("select t from Item i").getResultList();
-- ๋‹จ์ผ ํ…Œ์ด๋ธ” ์ „๋žต(InheritanceType.SINGLE_TABLE) SQL
SELECT * FROM ITEM

-- ์กฐ์ธ ์ „๋žต(InheritanceType.JOINED) SQL
SELECT 
    i.ITEM_ID, i.DTYPE, i.name, i.price, i.stockQuantity,
    b.author, b.isbn,
    b.artist, a.ect,
    m.actor, m.director
FROM
    ITEM i
LEFT OUTER JOIN
    BOOK b on i.ITEM_ID = b.ITEM_ID
LEFT OUTER JOIN
    ALBUM a on i.ITEM_ID = a.ITEM_ID
LEFT OUTER JOIN
    MOVIE M on i.ITEM_ID = m.ITEM_ID


TYPE
TYPE์€ ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ์† ๊ตฌ์กฐ์—์„œ ์กฐํšŒ ๋Œ€์ƒ์„ ํŠน์ • ์ž์‹ ํƒ€์ž…์œผ๋กœ ํ•œ์ •ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

-- Item ์ค‘์— Book, Movie๋ฅผ ์กฐํšŒํ•˜๋ผ.
-- JPQL 
select i from Item i
where type(i) IN (Book, Movie)

-- SQL
SELECT i FROM Item i
WHERE i.DTYPE in ('B','M')


TREAT (JPA 2.1)
์ž๋ฐ”์˜ ํƒ€์ž… ์บ์ŠคํŒ…๊ณผ ๋น„์Šทํ•œ๋‹ค. ์ƒ์† ๊ตฌ์กฐ์—์„œ ๋ถ€๋ชจ ํƒ€์ž…์„ ํŠน์ • ์ž์‹ ํƒ€์ž…์œผ๋กœ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
JPA ํ‘œ์ค€์€ FROM, WHERE ์ ˆ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” SELECT ์ ˆ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

-- JPQL
-- treat๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ถ€๋ชจ ํƒ€์ž…์ธ Item์„ ์ž์‹ ํƒ€์ž…์ธ Book์œผ๋กœ ๋‹ค๋ฃฌ๋‹ค.
-- ๋”ฐ๋ผ์„œ Book์˜ author ํ•„๋“œ์— ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋‹ค.
select i from Item i where treat(i as Book).author = 'kim'




10.2.12 ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ (JPA 2.1)

JPA 2.1๋ถ€ํ„ฐ ์‚ฌ์šฉ์ž ์ €์˜ ํ•จ์ˆ˜๋ฅผ ์ง€์›ํ•œ๋‹ค.

๋ฌธ๋ฒ•

function_invocation::= FUNCTION(function_name {, function_arg}*)

์˜ˆ

select function('group_concat', i.name) from Item i

-- ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ตฌํ˜„์ฒด๋Š” ์ถ•์•ฝ ๊ฐ€๋Šฅ
select group_concat(i.name) from Item i

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ฐฉ์–ธ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•ด์„œ ๊ตฌํ˜„ํ•˜๊ณ , ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ•จ์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.

// ๋ฐฉ์–ธ ํด๋ž˜์Šค ์ƒ์†
public class MyH2Dialect extends H2Dialect {
    public MyH2Dialect() {
        registerFunction(
            "group_concat",
            new StandardSQLFunction("group_concat", StandardBasicTypes.STRING)
        );
    }
}
<!-- ์ƒ์†ํ•œ ๋ฐฉ์–ธ ํด๋ž˜์Šค ๋“ฑ๋ก -->
<property name="hibernate.dialect" value="hello.MyH2Dialect">



10.2.13 ๊ธฐํƒ€ ์ •๋ฆฌ

  • enum์€ = ๋น„๊ต ์—ฐ์‚ฐ๋งŒ ์ง€์›ํ•œ๋‹ค.
  • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์€ ๋น„๊ต๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.

EMPTY STRING

  • JPA ํ‘œ์ค€์€ โ€˜โ€˜์„ ๊ธธ์ด 0์ธ Empty String์œผ๋กœ ์ •ํ–ˆ์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋”ฐ๋ผ โ€˜โ€˜๋ฅผ NULL๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋„ ์žˆ์œผ๋ฏ€๋กœ ํ™•์ธํ•˜๊ณ  ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

NULL ์ •์˜

  • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ํ•˜๋‚˜๋„ ์—†์œผ๋ฉด NULL์ด๋‹ค.
  • NULL์€ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฐ’(unknown value)์ด๋‹ค. NULL๊ณผ์˜ ๋ชจ๋“  ์ˆ˜ํ•™์  ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋Š” NULL์ด ๋œ๋‹ค.
  • NULL == NULL์€ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฐ’์ด๋‹ค.
  • NULL is NULL์€ ์ฐธ์ด๋‹ค.

JPA ํ‘œ์ค€ ๋ช…์„ธ NULL(U), TRUE(T), FALSE(F)์˜ ๋…ผ๋ฆฌ ๊ณ„์‚ฐ ํ‘œ

  • AND
AND T F U
T T F U
F F F F
U U F U
  • OR
OR T F U
T T T T
F T F U
U T U U
  • NOT
NOT ย 
T F
F T
U U



10.2.14 ์—”ํ‹ฐํ‹ฐ ์ง์ ‘ ์‚ฌ์šฉ

๊ธฐ๋ณธ ํ‚ค ๊ฐ’
JPQL์—์„œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋ฉด SQL์—์„œ๋Š” ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ์˜ ๊ธฐ๋ณธ ํ‚ค ๊ฐ’์„ ์‚ฌ์šฉํ•œ๋‹ค.

-- JPQL
-- ์—”ํ‹ฐํ‹ฐ์˜ ์•„์ด๋””๋ฅผ ์‚ฌ์šฉ
select count(m.id) from Member m

-- ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ
select count(m) from Member m
// ์—”ํ‹ฐํ‹ฐ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
String sql = "select m from Member m where m = :member";
List resultList = em.createQuery(sql)
            .setParameter("member", member)
            .getResultList();


// ์‹๋ณ„์ž ๊ฐ’์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
String sql = "select m from Member m where m.id = :memberId";
List resultList = em.createQuery(sql)
            .setParameter("memberId", 1L)
            .getResultList();


์™ธ๋ž˜ ํ‚ค ๊ฐ’
์™ธ๋ž˜ ํ‚ค ๊ฐ’๋„ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์—”ํ‹ฐํ‹ฐ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
String sql = "select m from Member m where m.team = :team";
List resultList = em.createQuery(sql)
            .setParameter("team", team)
            .getResultList();


// ์‹๋ณ„์ž ๊ฐ’์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
String sql = "select m from Member m where m.team.id = :teamId";
List resultList = em.createQuery(sql)
            .setParameter("teamId", 1L)
            .getResultList();



10.2.15 Named ์ฟผ๋ฆฌ: ์ •์  ์ฟผ๋ฆฌ

Named ์ฟผ๋ฆฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋”ฉ ์‹œ์ ์— JPQL ๋ฌธ๋ฒ•์„ ์ฒดํฌํ•˜๊ณ  ๋ฏธ๋ฆฌ ํŒŒ์‹ฑํ•ด๋‘”๋‹ค. ๋”ฐ๋ผ์„œ ์—๋Ÿฌ ์œ ๋ฌด๋ฅผ ๋นจ๋ฆฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ์žฌ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ์ƒ ์ด์ ๋„ ์žˆ๋‹ค.

Named ์ฟผ๋ฆฌ๋ฅผ ์–ด๋…ธํ…Œ์ด์…˜์— ์ •์˜

// @NamedQuery๋กœ Named ์ฟผ๋ฆฌ ์ •์˜
@Entity
@NamedQuery(
    name = "Member.findByUsername",
    query = "select m from Member m where m.username = :username"
)
public class Member {
    ...
}
// @NamedQueries๋ฅผ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ๊ฐœ์˜ @NamedQuery ์ง€์ •
@Entity
@NamedQueries(
    @NamedQuery(
        name = "Member.findByUsername",
        query = "select m from Member m where m.username = :username"
    ),
    @NamedQuery(
        name = "Member.count",
        query = "select count(m) from Member m"
    )
)
public class Member {
    ...
}
// ์‚ฌ์šฉ
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
                        .setPrameter("username", "ํšŒ์›1")
                        .getResultList();

Named ์ฟผ๋ฆฌ์— Member.findByUsername ์ฒ˜๋Ÿผ Member๋ฅผ ๋ถ™์ธ ๊ฒƒ์€ ๊ธฐ๋Šฅ์ ์œผ๋กœ ํŠน๋ณ„ํ•œ ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ์ถฉ๋™๋ฐฉ์ง€, ๊ด€๋ฆฌ์˜ ์šฉ์ดํ•จ์„ ์œ„ํ•ด ๋ถ™์ธ ๊ฒƒ์ด๋‹ค.






10.3 Criteria

Criteria ์ฟผ๋ฆฌ๋Š” JPQL์„ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ๋นŒ๋” ํด๋ž˜์Šค API๋‹ค. ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ์ฝ”๋“œ๋กœ JPQL์„ ์ž‘์„ฑํ•˜๋ฏ€๋กœ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๋ฅผ ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์žก์„ ์ˆ˜ ์žˆ๋‹ค. ๋™์  ์ฟผ๋ฆฌ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•˜๊ณ  ์žฅํ™ฉํ•ด์„œ ์ง๊ด€์ ์œผ๋กœ ์ดํ•ด๊ฐ€ ํž˜๋“ค๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

๋‚ด์šฉ ์ƒ๋žต ๐Ÿ˜…




10.4 QueryDSL

QueryDSL์€ Criteria ์ฒ˜๋Ÿผ JPQL ๋นŒ๋” ์—ญํ• ์„ ํ•˜๋Š”๋ฐ ์‰ฝ๊ณ  ๊ฐ„๊ฒฐํ•˜๋ฉฐ, ๊ทธ ๋ชจ์–‘๋„ ์ฟผ๋ฆฌ์™€ ๋น„์Šทํ•˜๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

QueryDSL์€ ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ์ด๋‹ค. ์•„๋ž˜ ์‚ฌ์ดํŠธ ์ฐธ๊ณ 


10.4.1 QueryDSL ์„ค์ •

ํ•„์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

<!-- QueryDSL JPA ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ -->
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-jpa</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<!-- ์ฟผ๋ฆฌ ํƒ€์ž…(Q)์„ ์ƒ์„ฑํ•  ๋•Œ ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ -->
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
  <version>${querydsl.version}</version>
  <scope>provided</scope>
</dependency>


ํ™˜๊ฒฝ์„ค์ •
QueryDSL์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฟผ๋ฆฌ ํƒ€์ž… ์ด๋ผ๋Š” ์ฟผ๋ฆฌ์šฉ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์Œ์ฒ˜๋Ÿผ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

<project>
  <build>
    <plugins>
        ...
        <plugin>
        <groupId>com.mysema.maven</groupId>
        <artifactId>apt-maven-plugin</artifactId>
        <version>1.1.3</version>
        <executions>
            <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
            </execution>
        </executions>
        </plugin>
        ...
    </plugins>
  </build>
</project>

์ด์ œ ์ฝ˜์†”์—์„œ mvc compile์„ ์ž…๋ ฅํ•˜๋ฉด outputDirectory์— ์ง€์ •ํ•œ target/generated-sources ์œ„์น˜์— QMember.java ์ฒ˜๋Ÿผ Q๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ฟผ๋ฆฌ ํƒ€์ž…๋“ค์ด ์ƒ์„ฑ๋œ๋‹ค.

10.4.2 ์‹œ์ž‘

๊ฐ„๋‹จ ์˜ˆ์ œ

public void queryDSL() {
    JPAQuery query = new JPAQuery(entityManager);
    QMember qMember = new QMember("m"); // ์ƒ์„ฑ๋˜๋Š” JPQL์˜ ๋ณ„์นญ์ด m
    List<Member> members = query.from(qMember)
                                .where(qMember.name.eq("ํšŒ์›1"))
                                .orderBy(qMember.name.desc)
                                .list(qMember);
}


๊ธฐ๋ณธ Q ์ƒ์„ฑ
์ฟผ๋ฆฌ ํƒ€์ž…(Q)๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ํŽธ๋ฆฌํ•˜๋„๋ก ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ธฐ๋ณธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ณด๊ด€ํ•˜๊ณ  ์žˆ๋‹ค.

public class QMember extends EntityPathBase<Member> {
    public static final QMember member = new QMember("member1");
    ...
}

๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐ์ธํ•˜๊ฑฐ๋‚˜, ๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์„œ๋ธŒ์ฟผ๋ฆฌ์— ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ™์€ ๋ณ„์นญ์ด ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ์ด๋•Œ๋Š” ๋ณ„์ง•์„ ์ง์ ‘ ์ง€์ •ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

QMember qMember = new QMember("m");  // ์ง์ ‘ ์ง€์ •
QMember qMember = QMember.member;    // ๊ธฐ๋ณธ ์ธ์Šคํ„ด์Šค ์‚ฌ์šฉ

๋‹ค์Œ๊ณผ ๊ฐ™์ด import static์„ ํ™œ์šฉํ•˜๋ฉด ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค.

import static jpabook.jpashop.domain.QMember.member; // ๊ธฐ๋ณธ ์ธ์Šคํ„ด์Šค

public void basic() {
    JPAQuery query = new JPAQuery(entityManager);
    List<Member> members = query.from(member)  // member ๋ฐ”๋กœ ์‚ฌ์šฉ
                        .where(member.name.eq("ํšŒ์›1"))
                        .orderBy(member.name.desc)
                        .list(member);
}



10.4.3 ๊ฒ€์ƒ‰ ์กฐ๊ฑด ์ฟผ๋ฆฌ

// where and
query.from(customer)
    .where(customer.firstName.eq("Bob").and(customer.lastName.eq("Wilson")));

// where or
query.from(customer)
    .where(customer.firstName.eq("Bob").or(customer.lastName.eq("Wilson")));

// between
// 1800 ~ 2000 ๋…„๋„
doc.year.between("1800", "2000");

// contains
// sql์˜ like '%์ƒํ’ˆ1%'
item.name.contains("์ƒํ’ˆ1");

// startsWith
// sql์˜ like '์˜ค%'
person.firstName.startsWith("์˜ค");



10.4.4 ๊ฒฐ๊ณผ ์กฐํšŒ

์ฟผ๋ฆฌ ์ž‘์„ฑ์ด ๋๋‚˜๊ณ  ๊ฒฐ๊ณผ ์กฐํšŒ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•œ๋‹ค. ๊ฒฐ๊ณผ ์กฐํšŒ API๋Š” com.mysema.query.Projectable์— ์ •์˜๋˜์–ด ์žˆ๋‹ค.

  • uniqueResult(): ์กฐํšŒ ๊ฒฐ๊ณผ๊ฐ€ ํ•œ ๊ฑด์ผ ๋•Œ ์‚ฌ์šฉ. ์กฐํšŒ ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ํ•˜๋‚˜ ์ด์ƒ์ด๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ
  • singleResult(): uniqueResult()์™€ ๊ฐ™์ง€๋งŒ ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์ด๋ฉด ์ฒ˜์Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜
  • list(): ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์ผ ๋•Œ ์‚ฌ์šฉ. ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ๋นˆ ์ปฌ๋ ‰์…˜ ๋ฐ˜ํ™˜



10.4.5 ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ

query.from(item)
    .where(item.price.gt(20000))
    .orderBy(item.price.desc(), item.stockQuantity.asc())
    .offset(10).limit(20)
    .list(item);

์‹ค์ œ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ ค๋ฉด ๊ฒ€์ƒ‰๋œ ์ „์ฒด ๋ฐ์ดํ„ฐ ์ˆ˜๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. ์ด๋•Œ๋Š” list() ๋Œ€์‹ ์— listResults()๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

SearchResults<Item> result = query.from(item)
                .where(item.price.gt(20000))
                .orderBy(item.price.desc(), item.stockQuantity.asc())
                .offset(10).limit(20)
                .listResults(item);

long total = result.getTotal(); // ๊ฒ€์ƒ‰๋œ ์ „์ฒด ๋ฐ์ดํ„ฐ ์ˆ˜
List<Item> results = result.getResults(); // ์กฐํšŒ๋œ ๋ฐ์ดํ„ฐ



10.4.6 ๊ทธ๋ฃน

query.from(item)
    .groupBy(item.price)
    .having(item.price.gt(1000))
    .list(item);



10.4.7 ์กฐ์ธ

์กฐ์ธ์€ innerJoin, leftJoin, rightJoin, fullJoin์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ถ”๊ฐ€๋กœ JPQL์˜ fetch์กฐ์ธ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์กฐ์ธ์˜ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•์€ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์กฐ์ธ ๋Œ€์ƒ์„ ์ง€์ •ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋ณ„์นญ์œผ๋กœ ์‚ฌ์šฉํ•  ์ฟผ๋ฆฌ ํƒ€์ž…์„ ์ง€์ •ํ•œ๋‹ค.

join(์กฐ์ธ๋Œ€์ƒ, ๋ณ„์นญ์œผ๋กœ ์‚ฌ์šฉํ•  ์ฟผ๋ฆฌ ํƒ€์ž…)
// ๊ธฐ๋ณธ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
QCat cat = QCat.cat;
QCat mate = new QCat("mate");
QCate kitten = new QCat("kitten");
query.from(cat)
    .innerJoin(cat.mate, mate)
    .leftJoin(cat.kittens, kitten)
    .list(cat);


// ์กฐ์ธ on ์‚ฌ์šฉ
query.from(cat)
    .leftJoin(cat.kittens, kitten)
    .on(kitten.bodyWeight.gt(10.0))
    .list(cat);


// ํŽ˜์น˜ ์กฐ์ธ ์‚ฌ์šฉ
query.from(cat)
    .innerJoin(cat.mate, mate).fetch()
    .leftJoin(cat.kittens, kitten).fetch()
    .list(cat);

// ์„ธํƒ€ ์กฐ์ธ ์‚ฌ์šฉ
query.from(cat, mate)
    .where(cat.mate.eq(mate))
    .list(cat);



10.4.8 ์„œ๋ธŒ ์ฟผ๋ฆฌ

  • ์„œ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด com.mysema.query.jpa.JPASubQuery๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ์„œ๋ธŒ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜๋ฉด unique(), ์—ฌ๋Ÿฌ ๊ฑด์ด๋ฉด list()๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
// ์„œ๋ธŒ ์ฟผ๋ฆฌ ํ•œ ๊ฑด
QItem item = QItem.item;
QItem itemSub = new QItem("itemSub");

query.from(item)
    .where(item.price.eq(
        new JPASubQuery().from(itemSub).unique(itemSub.price.max())
    ))
    .list(item);



// ์„œ๋ธŒ ์ฟผ๋ฆฌ ์—ฌ๋Ÿฌ ๊ฑด
QItem item = QItem.item;
QItem itemSub = new QItem("itemSub");

query.from(item)
    .where(item.in(
        new JPASubQuery().from(itemSub)
            .where(item.name.eq(itemSub.name))
            .list(itemSub)
    ))
    .list(item);



10.4.9 ํ”„๋กœ์ ์…˜๊ณผ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜

select ์ ˆ์— ์กฐํšŒ ๋Œ€์ƒ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์„ ํ”„๋กœ์ ์…˜์ด๋ผ ํ•œ๋‹ค.

ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์ด ํ•˜๋‚˜
ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์ด ํ•˜๋‚˜๋ฉด ํ•ด๋‹น ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

QItem item = QItem.item;
List<String> result = query.from(item).list(item.name);


์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ ๋ฐ˜ํ™˜๊ณ  ํŠœํ”Œ
ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ์ด ์—ฌ๋Ÿฌ ํ•„๋“œ๋ฅผ ์„ ํƒํ•˜๋ฉด QueryDSL์€ ๊ธฐ๋ณธ์ ์œผ๋กœ com.mysema.query.Tuple์ด๋ผ๋Š” ๋‚ด๋ถ€ ํƒ€์ž…์„ ์‚ฌ์šฉํ•œ๋‹ค.

QItem item = QItem.item;
List<Tuple> result = query.from(item).list(item.name, item.price);

for (Tuple tuple : result) {
    tuple.get(item.name);   // name
    tuple.get(item.price);  // price
}


๋นˆ ์ƒ์„ฑ
์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์•„๋‹Œ ํŠน์ • ๊ฐ์ฒด๋กœ ๋ฐ›๊ณ  ์‹ถ์œผ๋ฉด ๋นˆ ์ƒ์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค. QueryDSL์€ ๋‹ค์Œ์„ ์‚ฌ์šฉํ•ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

  • ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ
  • ํ•„๋“œ ์ง์ ‘ ์ ‘๊ทผ
  • ์ƒ์„ฑ์ž ์‚ฌ์šฉ
public class ItemDTO {
    private String username;
    private String price;

    // ์ƒ์„ฑ์ž ...
    // Getter, Setter ...
}


ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ

QItem item = QItem.item;
List<ItemDTO> result = query.from(item)
.list(Projections.bean(ItemDTO.class, item.name.as("username"), item.price));

Projections.bean()๋ฉ”์†Œ๋“œ๋Š” Setter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ’์„ ์ฑ„์šด๋‹ค.

ํ•„๋“œ ์ง์ ‘ ์ ‘๊ทผ

QItem item = QItem.item;
List<ItemDTO> result = query.from(item)
.list(Projections.fields(ItemDTO.class, item.name.as("username"), item.price));

Projections.fields()๋ฉ”์†Œ๋“œ๋Š” ํ•„๋“œ์— ์ง์ ‘ ์ ‘๊ทผํ•ด์„œ ๊ฐ’์„ ์ฑ„์šด๋‹ค.

์ƒ์„ฑ์ž ์‚ฌ์šฉ

QItem item = QItem.item;
List<ItemDTO> result = query.from(item)
.list(Projections.constructor(ItemDTO.class, item.name, item.price));

์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ง€์ •ํ•œ ํ”„๋กœ์ ์…˜๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆœ์„œ๊ฐ€ ๊ฐ™์€ ์ƒ์„ฑ์ž๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

DISTINCT distinct๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.

query.distinct().from(item)...



10.4.10 ์ˆ˜์ •, ์‚ญ์ œ ๋ฐฐ์น˜ ์ฟผ๋ฆฌ

QuertDSL๋„ ์ˆ˜์ •, ์‚ญ์ œ ๊ฐ™์€ ๋ฐฐ์น˜ ์ฟผ๋ฆฌ๋ฅผ ์ง€์›ํ•œ๋‹ค. JPQL ๋ฐฐ์น˜ ์ฟผ๋ฆฌ์™€ ๊ฐ™์ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ง์ ‘ ์ฟผ๋ฆฌํ•œ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์ž.

์ˆ˜์ • ๋ฐฐ์น˜ ์ฟผ๋ฆฌ

QItem item = QItem.item;
JPAUpdateClause updateClause = new JPAUpdateClause(em, item);
long count = updateClause.where(item.name.eq("์‹œ๊ณจ๊ฐœ๋ฐœ์ž์˜ JPA์ฑ…"))
                    .set(item.price, item.price.add(100))
                    .execute();


์‚ญ์ œ ๋ฐฐ์น˜ ์ฟผ๋ฆฌ

QItem item = QItem.item;
JPADeleteClause deleteClause = new JPADeleteClause(em, item);
long count = deleteClause.where(item.name.eq("์‹œ๊ณจ๊ฐœ๋ฐœ์ž์˜ JPA์ฑ…")).execute();



10.4.11 ๋™์  ์ฟผ๋ฆฌ

com.mysema.query.BooleanBuilder๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ฅธ ๋™์  ์ฟผ๋ฆฌ๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

SearchParam param = new SearchParam();
param.setName("์‹œ๊ณจ๊ฐœ๋ฐœ์ž");
param.setPrice(10000);

QItem item = QItem.item;

BooleanBuilder builder = new BooleanBuilder();
if (StringUtils.hasText(param.getName())) {
    builder.and(item.name.contains(param.getName()));
}

if (param.getPrice != null) {
    builder.and(item.price.gt(param.getPrice()));
}

List<Item> result = query.from(item).where(builder).list(item);



10.4.12 ๋ฉ”์†Œ๋“œ ์œ„์ž„

๋ฉ”์†Œ๋“œ ์œ„์ž„ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ ํƒ€์ž…์— ๊ฒ€์ƒ‰ ์กฐ๊ฑด์„ ์ง์ ‘ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฒ€์ƒ‰ ์กฐ๊ฑด ์ •์˜

public class ItemExpression {

    @QueryDelegate(Item.class)
    public static BooleanExpression isExpensive(QItem item, Integer price) {
        return item.price.gt(price);
    }
}
  • ๋ฉ”์†Œ๋“œ ์œ„์ž„ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ •์ (static) ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  @QueryDelegate ์–ด๋…ธํ…Œ์ด์…˜์— ์†์„ฑ์œผ๋กœ ์ด ๊ธฐ๋Šฅ์„ ์ ์šฉํ•  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง€์ •ํ•œ๋‹ค.
  • ์ •์  ๋ฉ”์†Œ๋“œ์˜ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ์˜ ์ฟผ๋ฆฌ ํƒ€์ž…(Q)์„ ์ง€์ •ํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ •์˜ํ•œ๋‹ค.

์ฟผ๋ฆฌ ํƒ€์ž…(Q)์— ์ƒ์„ฑ๋œ ๊ฒฐ๊ณผ
QItem์— ์ƒ์„ฑ๋œ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

public class QItem extends EntityPathBase<ITem> {
    ...
    public com.mysema.query.types.expr.BooleanExpression isExpensive(Integer price) {
        return ItemExpression.isExpensive(this, price);
    }
}

์‚ฌ์šฉ

query.from(item).where(item.isExpensive(30000)).list(item);


ํ•„์š”ํ•˜๋‹ค๋ฉด String, Date ๊ฐ™์€ ์ž๋ฐ” ๊ธฐ๋ณธ ๋‚ด์žฅ ํƒ€์ž…์—๋„ ๋ฉ”์†Œ๋“œ ์œ„์ž„ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@QueryDelegate(String.class)
    public static BooleanExpression isHelloStart(StringPath stringPath) {
        return stringPath.startsWith("Hello");
}





10.5 ๋„ค์ดํ‹ฐ๋ธŒ SQL

JPQL์€ ํ‘œ์ค€ SQL์ด ์ง€์›ํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๋ฌธ๋ฒ•๊ณผ SQL ํ•จ์ˆ˜๋“ค์„ ์ง€์›ํ•˜์ง€๋งŒ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ข…์†์ ์ธ ๊ธฐ๋Šฅ์€ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ๋“ค์ด๋‹ค.

  • ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ ์ง€์›ํ•˜๋Š” ํ•จ์ˆ˜, ๋ฌธ๋ฒ•, SQL์ฟผ๋ฆฌ ํžŒํŠธ
  • ์ธ๋ผ์ธ๋ทฐ(From ์ ˆ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ธŒ์ฟผ๋ฆฌ), UNION, INTERSECT
  • ์Šคํ† ์–ด๋“œ ํ”„๋กœ์‹œ์ €

๋•Œ๋กœ๋Š” ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ข…์†์ ์ธ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜๋‹ค. JPA ๊ตฌํ˜„์ฒด๋“ค์€ JPA ํ‘œ์ค€๋ณด๋‹ค ๋” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์ง€์›ํ•œ๋‹ค.

  • ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜
    • JPQL์—์„œ ๋„ค์ดํ‹ฐ๋ธŒ SQL ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.(JPA 2.1)
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฉ์–ธ์— ๊ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ข…์†์ ์ธ ํ•จ์ˆ˜๋“ค์„ ์ •์˜ํ•ด๋‘์—ˆ๋‹ค. ๋˜ํ•œ ์ง์ ‘ ํ˜ธ์ถœํ•  ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ ์ง€์›ํ•˜๋Š” SQL ์ฟผ๋ฆฌ ํžŒํŠธ
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ํฌํ•จํ•œ ๋ช‡๋ช‡ JPA ๊ตฌํ˜„์ฒด๋“ค์ด ์ง€์›ํ•œ๋‹ค.
  • ์ธ๋ผ์ธ ๋ทฐ(From ์ ˆ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ธŒ์ฟผ๋ฆฌ), UNION, INTERSECT
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ ๋ช‡๋ช‡ JPA ๊ตฌํ˜„์ฒด๋“ค์ด ์ง€์›ํ•œ๋‹ค.
  • ์Šคํ† ์–ด ํ”„๋กœ์‹œ์ €
    • JPQL์—์„œ ์Šคํ† ์–ด๋“œ ํ”„๋กœ์‹œ์ €๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.(JPA 2.1)
  • ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ ์ง€์›ํ•˜๋Š” ๋ฌธ๋ฒ•
    • ์ด๋•Œ๋Š” ๋„ค์ดํ‹ฐ๋ธŒ SQL์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.


๋‹ค์–‘ํ•œ ์ด์œ ๋กœ JPQL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์„ ๋•Œ JPA๋Š” SQL์„ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ ์ด๋ฅผ ๋„ค์ดํ‹ฐ๋ธŒ SQL์ด๋ผ ํ•œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๋„ค์ดํ‹ฐ๋ธŒ SQL๊ณผ JDBC API๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ์˜ ์ฐจ์ด์ ์€ ๋ฌด์—‡์ผ๊นŒ? ๋„ค์ดํ‹ฐ๋ธŒ SQL์„ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ณ , JPA๊ฐ€ ์ง€์›ํ•˜๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๊ธฐ๋Šฅ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

10.5.1 ๋„ค์ดํ‹ฐ๋ธŒ SQL ์‚ฌ์šฉ

// ๊ฒฐ๊ณผ ํƒ€์ž… ์ •์˜
public Query createNativeQuery(String sqlString, Calss resultClass);

// ๊ฒฐ๊ณผ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์—†์„ ๋•Œ
public Query createNativeQuery(String sqlString);

// ๊ฒฐ๊ณผ ๋งคํ•‘ ์‚ฌ์šฉ
public Query createNativeQuery(String sqlString, String resultSetMapping);

๋‚ด์šฉ ์ƒ๋žต ๐Ÿ˜…

10.6 ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์‹ฌํ™”

10.6.1 ๋ฒŒํฌ ์—ฐ์‚ฐ

ํ•œ ๋ฒˆ์— ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๋Š” ๋ฒŒํฌ ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

UPDATE ๋ฒŒํฌ ์—ฐ์‚ฐ

String sqlString;
    "update Product p " +
    "set p.price = p.price * 1.1 " +
    "where p.stockAmount < :stockAmount";

int count = em.createQuery(sqlString)
            .setParameter("stockAmount", 10)
            .executeUpdate();


DELETE ๋ฒŒํฌ ์—ฐ์‚ฐ

String sqlString;
    "delete from Product p " +
    "where p.price < :price";

int count = em.createQuery(sqlString)
            .setParameter("price", 100)
            .executeUpdate();


๋ฒŒํฌ ์—ฐ์‚ฐ์˜ ์ฃผ์˜์ 
๋ฒŒํฌ ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ง์ ‘ ์ฟผ๋ฆฌํ•œ๋‹ค๋Š” ์ ์— ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  • em.refresh() ์‚ฌ์šฉ
  • ๋ฒŒํฌ ์—ฐ์‚ฐ ๋จผ์ € ์‹คํ–‰
  • ๋ฒŒํฌ ์—ฐ์‚ฐ ์ˆ˜ํ–‰ ํ›„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ดˆ๊ธฐํ™”



10.6.2 ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ JPQL

์ฟผ๋ฆฌ ํ›„ ์˜์† ์ƒํƒœ์ธ ๊ฒƒ๊ณผ ์•„๋‹Œ ๊ฒƒ

  • ์˜์† ์ƒํƒœ
    • ์—”ํ‹ฐํ‹ฐ
  • ์˜์† ์ƒํƒœ ์•„๋‹Œ ๊ฒƒ
    • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…
    • ๋‹จ์ˆœ ํ•„๋“œ ์กฐํšŒ


em.find() ๋™์ž‘ ์ˆœ์„œ

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋จผ์ € ์ฐพ๊ณ 
  • ์—†์œผ๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ฐพ๋Š”๋‹ค.


JPQL ๋™์ž‘ ์ˆœ์„œ

  • ์ตœ์ดˆ ์กฐํšŒ
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒ
    • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋ก
  • ๋‘ ๋ฒˆ์งธ ์กฐํšŒ
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒ
    • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ ๋ฐœ๊ฒฌ
    • ์ƒˆ๋กœ ๊ฒ€์ƒ‰ํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ๋ฒ„๋ฆฌ๊ณ  ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์žˆ๋Š” ๊ธฐ์กด ์—”ํ‹ฐํ‹ฐ ๋ฐ˜ํ™˜


JPQL ํŠน์ง• ์ •๋ฆฌ

  • JPQL์€ ํ•ญ์ƒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • JPQL๋กœ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ์˜์† ์ƒํƒœ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๊ธฐ์กด ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.



10.6.3 JPQL๊ณผ ํ”Œ๋Ÿฌ์‹œ ๋ชจ๋“œ

ํ”Œ๋Ÿฌ์‹œ ๋ชจ๋“œ

em.setFlushMode(FlushModeType.AUTO);    // ์ปค๋ฐ‹, ์ฟผ๋ฆฌ ์‹คํ–‰ ์‹œ ํ”Œ๋Ÿฌ์‹œ (๊ธฐ๋ณธ๊ฐ’)
em.setFlushMode(FlushModeType.COMMIT);  // ์ปค๋ฐ‹์‹œ์—๋งŒ ํ”Œ๋Ÿฌ์‹œ
  • JPQL์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ JPQL์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ํ”Œ๋Ÿฌ์‹œ๋ฅผ ํ†ตํ•ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•ด์•ผ ํ•œ๋‹ค.


10.7 ์ •๋ฆฌ

  • JPQL์€ SQL์„ ์ถ”์ƒํ™”ํ•ด์„œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ์ˆ ์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • Criteria๋‚˜ QueryDSL์€ JPQL์„ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋นŒ๋” ์—ญํ• ์„ ํ•  ๋ฟ์ด๋ฏ€๋กœ ํ•ต์‹ฌ์€ JPQL์„ ์ž˜ ์•Œ์•„์•ผ ํ•œ๋‹ค.
  • Criteria๋‚˜ QueryDSL์„ ์‚ฌ์šฉํ•˜๋ฉด ๋™์ ์œผ๋กœ ๋ณ€ํ•˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Criteria๋Š” JPA๊ฐ€ ๊ณต์‹ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์ด์ง€๋งŒ ๋ถˆํŽธํ•˜๋‹ค. QueryDSL์€ JPA๊ฐ€ ๊ณต์‹ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์€ ์•„๋‹ˆ์ง€๋งŒ ํŽธ๋ฆฌํ•˜๋‹ค.
  • JPA๋„ ๋„ค์ดํ‹ฐ๋ธŒ SQL์„ ์ œ๊ณตํ•˜๋ฏ€๋กœ ์ง์ ‘ SQL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ข…์†์ ์ด ๋˜๋ฏ€๋กœ ์ตœ๋Œ€ํ•œ ์‚ฌ์šฉ์„ ์ž์ œํ•˜์ž.
  • JPQL์€ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๋Š” ๋ฒŒํฌ ์—ฐ์‚ฐ์„ ์ง€์›ํ•œ๋‹ค.




11. ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œ์ž‘

๋‚ด์šฉ ์ƒ๋žต ๐Ÿ˜…



12. ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA

12.1 ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์†Œ๊ฐœ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๋ ˆํผ๋Ÿฐ์Šค ๋ฌธ์„œ

  • ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ JPA๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋‹ค.
  • CURD๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ž‘์„ฑํ•˜๋ฉด ์‹คํ–‰ ์‹œ์ ์— ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•ด์„œ ์ฃผ์ž…ํ•ด์ค€๋‹ค.
  • ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์„ ๊ฐœ๋ฐœํ•  ๋•Œ ๊ตฌํ˜„ ํด๋ž˜์Šค ์—†์ด ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ž‘์„ฑํ•ด๋„ ๊ฐœ๋ฐœ์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ๋‹ค.


12.2 ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์„ค์ •

ํ•„์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

<!-- ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>${spring-data-jpa.version}</version>
</dependency>


ํ™˜๊ฒฝ ์„ค์ •

<!-- xml -->
<!-- ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ•ด๋‹น ํŒจํ‚ค์ง€์™€ ๊ทธ ํ•˜์œ„ ํŒจํ‚ค์ง€์—์„œ ๊ฒ€์ƒ‰ํ•œ๋‹ค. -->
<jpa:repositories base-package="jpabook.jpashop.repository" />
// java config class
@Configuration
@EnableJpaRepositories(basePackage = "jpabook.jpashop.repository")
public class AppConfig {}



12.3 ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋Šฅ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ๊ฐ„๋‹จํ•œ CRUD ๊ธฐ๋Šฅ์„ ๊ณตํ†ต์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” JpaRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

// JpaRepository ์ธํ„ฐํŽ˜์ด์Šค
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {...}
// JpaRepository ์ธํ„ฐํŽ˜์ด์Šค ์‚ฌ์šฉ
public interface MemberRepository extends JpaRepository<Member, Long> {}



12.4 ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ ๊ธฐ๋Šฅ

  • ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ฟผ๋ฆฌ ์ƒ์„ฑ
  • ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ JPA NamedQuery ํ˜ธ์ถœ
  • @Query ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค์— ์ฟผ๋ฆฌ ์ง์ ‘ ์ •์˜


12.4.1 ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ฟผ๋ฆฌ ์ƒ์„ฑ

// ์ด๋ฉ”์ผ๊ณผ ์ด๋ฆ„์œผ๋กœ ํšŒ์›์„ ์กฐํšŒ ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜
public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByEmailAndName(String email, String name);
}
-- ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ๋ฉ”์†Œ๋“œ๋ฅผ ๋ถ„์„ํ•ด์„œ JPQL์„ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•œ๋‹ค.
select m from Member m where m.email =?1 and m.name = ?2

๊ทœ์น™
์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์ฟผ๋ฆฌ ์ƒ์„ฑ ๊ธฐ๋Šฅ

ํ‚ค์›Œ๋“œ ์˜ˆ JPQL ์˜ˆ

Distinct

findDistinctByLastnameAndFirstname

select distinct โ€ฆโ€‹ where x.lastname = ?1 and x.firstname = ?2

And

findByLastnameAndFirstname

โ€ฆ where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

โ€ฆ where x.lastname = ?1 or x.firstname = ?2

Is, Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

โ€ฆ where x.firstname = ?1

Between

findByStartDateBetween

โ€ฆ where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

โ€ฆ where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

โ€ฆ where x.age <= ?1

GreaterThan

findByAgeGreaterThan

โ€ฆ where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

โ€ฆ where x.age >= ?1

After

findByStartDateAfter

โ€ฆ where x.startDate > ?1

Before

findByStartDateBefore

โ€ฆ where x.startDate < ?1

IsNull, Null

findByAge(Is)Null

โ€ฆ where x.age is null

IsNotNull, NotNull

findByAge(Is)NotNull

โ€ฆ where x.age not null

Like

findByFirstnameLike

โ€ฆ where x.firstname like ?1

NotLike

findByFirstnameNotLike

โ€ฆ where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

โ€ฆ where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

โ€ฆ where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

โ€ฆ where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

โ€ฆ where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

โ€ฆ where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

โ€ฆ where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

โ€ฆ where x.age not in ?1

True

findByActiveTrue()

โ€ฆ where x.active = true

False

findByActiveFalse()

โ€ฆ where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

โ€ฆ where UPPER(x.firstname) = UPPER(?1)


12.4.2 JPA NamedQuery

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ JPA Named ์ฟผ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

// @NamedQuery๋กœ Named ์ฟผ๋ฆฌ ์ •์˜
@Entity
@NamedQuery(
    name = "Member.findByUsername",
    query = "select m from Member m where m.username = :username"
)
public class Member {
    ...
}
// ๋„๋ฉ”์ธ ํด๋ž˜์Šค + .(์ ) + ๋ฉ”์†Œ๋“œ ์ด๋ฆ„ ์œผ๋กœ Named ์ฟผ๋ฆฌ๋ฅผ ์ฐพ์•„ ์‹คํ–‰ํ•œ๋‹ค.
// Named ์ฟผ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์œผ๋กœ ์ฟผ๋ฆฌ ์ƒ์„ฑ ์ „๋žต์„ ์‚ฌ์šฉํ•œ๋‹ค.
public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsername(String username);
}


12.4.3 @Query, ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ์— ์ฟผ๋ฆฌ ์ •์˜

๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ์— ์ง์ ‘ ์ฟผ๋ฆฌ๋ฅผ ์ •์˜ํ•˜๋ ค๋ฉด @Query ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

// ๋ฉ”์†Œ๋“œ์— JPQL ์ฟผ๋ฆฌ ์ž‘์„ฑ
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select m from Member m where m.username = ?1")
    Member findByUsername(String username);
}
// JPA ๋„ค์ดํ‹ฐ๋ธŒ SQL ์ง€์›
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("SELECT * FROM MEMBER FROM WHERE USERNAME = ?0", nativeQuery = true)
    Member findByUsername(String username);
}



12.4.4 ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ์œ„์น˜ ๊ธฐ๋ฐ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ๊ณผ ์ด๋ฆ„ ๊ธฐ๋ฐ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์ง€์›ํ•œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ ์œ„์น˜ ๊ธฐ๋ฐ˜์ด๋‹ค.

-- ์œ„์น˜ ๊ธฐ๋ฐ˜, ๊ธฐ๋ณธ๊ฐ’
select m from Member m where m.username = ?1

-- ์ด๋ฆ„ ๊ธฐ๋ฐ˜
select m from Member m where m.username = :name
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select m from Member m where m.username = :name")
    Member findByUsername(@Param("name") String username);
}



12.4.5 ๋ฒŒํฌ์„ฑ ์ˆ˜์ • ์ฟผ๋ฆฌ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ์‚ฌ์šฉํ•œ ๋ฒŒํฌ์„ฑ ์ˆ˜์ •, ์‚ญ์ œ ์ฟผ๋ฆฌ๋Š” @Modifying ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

@Modifying
@Query("update Product p set p.price = price * 1.1 where p.stockAmount < :stockAmount")
int bulkPriceUp(@Param("stockAmount") String stockAmount);

๋ฒŒํฌ์„ฑ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋‚˜์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์‹ถ์œผ๋ฉด @Modifying(clearAutomatically = true) ์˜ต์…˜์„ ์ค€๋‹ค.

12.4.6 ๋ฐ˜ํ™˜ ํƒ€์ž…

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ์œ ์—ฐํ•œ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ง€์›ํ•œ๋‹ค. ํ•œ ๊ฑด ์ด์ƒ์ด๋ฉด ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๊ณ , ๋‹จ๊ฑด์ด๋ฉด ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ง€์ •ํ•œ๋‹ค.

List<Member> findByName(String name); // ์ปฌ๋ ‰์…˜
Member findByEmail(String email);     // ๋‹จ๊ฑด

์กฐํšŒ ๊ฒฐ๊ณผ๊ฐ€ ์—†์„ ์‹œ

  • ์ปฌ๋ ‰์…˜: ๋นˆ ์ปฌ๋ ‰์…˜ ๋ฐ˜ํ™˜
  • ๋‹จ๊ฑด: null ๋ฐ˜ํ™˜

์กฐํšŒ ๊ฒฐ๊ณผ 1๊ฑด

  • ์ปฌ๋ ‰์…˜: 1๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„ ์ปฌ๋ ‰์…˜ ๋ฐ˜ํ™˜
  • ๋‹จ๊ฑด: ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜

์กฐํšŒ ๊ฒฐ๊ณผ 2๊ฑด ์ด์ƒ

  • ์ปฌ๋ ‰์…˜: 2๊ฑด ์ด์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„ ์ปฌ๋ ‰์…˜ ๋ฐ˜ํ™˜
  • ๋‹จ๊ฑด: ์˜ˆ์™ธ ๋ฐœ์ƒ


12.4.7 ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ์— ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก 2๊ฐ€์ง€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

  • org.springframework.data.domain.Sort: ์ •๋ ฌ๊ธฐ๋Šฅ
  • org.springframework.data.domain.Pageable: ํŽ˜์ด์ง• ๊ธฐ๋Šฅ(๋‚ด๋ถ€์— Sort ํฌํ•จ)

ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ ์‚ฌ์šฉ ์˜ˆ

// count ์ฟผ๋ฆฌ ์‚ฌ์šฉ
Page<Member> findByName(String name, Pageable pageable);

// count ์ฟผ๋ฆฌ ์‚ฌ์šฉ ์•ˆ ํ•จ
// total์„ ์ œ์™ธํ•œ Page์—์„œ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์‚ฌ์šฉ ๊ฐ€๋Šฅ
Slice<Member> findByName(String name, Pageable pageable);

// count ์ฟผ๋ฆฌ ์‚ฌ์šฉ ์•ˆ ํ•จ
List<Member> findByName(String name, Pageable pageable);

// ์ •๋ ฌ
List<Member> findByName(String name, Sort sort);
// ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
PageRequest pageRequest = new PageRequest(0, 10, new Sort(Direction.DESC, "name"));
Page<Member> result = memberRepository.findByName("๊น€", pageRequest);


Slice ์ธํ„ฐํŽ˜์ด์Šค

public interface Slice<T> extends Streamable<T> {
    int getNumber(); // ํ˜„์žฌ ํŽ˜์ด์ง€
    int getSize(); //ํŽ˜์ด์ง€ ํฌ๊ธฐ
    int getNumberOfElements(); // ํ˜„์žฌ ํŽ˜์ด์ง€์— ์กฐํšŒํ•œ ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜
    List<T> getContent(); // ํ˜„์žฌ ํŽ˜์ด์ง€์— ์กฐํšŒํ•œ ๋ฐ์ดํ„ฐ 
    boolean hasContent(); // ํ˜„์žฌ ํŽ˜์ด์ง€์— ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€ 
    Sort getSort(); // ์ •๋ ฌ ์—ฌ๋ถ€ 
    boolean isFirst(); // ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€์ธ์ง€ ์—ฌ๋ถ€
    boolean isLast(); // ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€์ธ์ง€ ์—ฌ๋ถ€
    boolean hasNext(); // ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€
    boolean hasPrevious();  // ์ด์ „ ํŽ˜์ด์ง€๊ฐ€ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€ 
}


Page ์ธํ„ฐํŽ˜์ด์Šค

public interface Page<T> extends Slice<T> {
    static <T> Page<T> empty() {
        return empty(Pageable.unpaged());
    }

    static <T> Page<T> empty(Pageable pageable) {
        return new PageImpl<>(Collections.emptyList(), pageable, 0);
    }

    int getTotalPages();     // ์ „์ฒด ํŽ˜์ด์ง€ ๊ฐœ์ˆ˜
    long getTotalElements(); // ์ „์ฒด ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜
    <U> Page<U> map(Function<? super T, ? extends U> converter);
}



12.4.8 ํžŒํŠธ

JPA ์ฟผ๋ฆฌ ํžŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @QueryHint์–ด๋…ธ์—ํ‹ฐ์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๊ฒƒ์€ SQL ํžŒํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ JPA๊ตฌํ˜„์ฒด์—๊ฒŒ ์ œ๊ณตํ•˜๋Š” ํžŒํŠธ๋‹ค.

@QueryHints(value = {
    @QueryHint(name = "org.hibernate.readOnly", value = true)
})
Page<Member> findByName(String name, Pageable pageable);




12.5 ๋ช…์„ธ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” JPA Criteria๋กœ ๋ช…์„ธ(SPECIFICATION)์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•œ๋‹ค. ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ์ด์šฉํ•œ ์กฐํšŒ ๊ธฐ๋Šฅ ์ฐธ๊ณ 


12.6 ์‚ฌ์šฉ์ž ์ •์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๊ตฌํ˜„

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋กœ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๊ฐœ๋ฐœํ•˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ •์˜ํ•˜๊ณ  ๊ตฌํ˜„์ฒด๋Š” ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. ํ•˜์ง€๋งŒ ๋ฉ”์†Œ๋“œ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๋•Œ๋„ ์žˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ

// ์ธํ„ฐํŽ˜์ด์Šค ์ด๋ฆ„์€ ์ž์œ ๋กญ๊ฒŒ ์ž‘์„ฑ ๊ฐ€๋Šฅ
public interface MemberRepositoryCustom {
    public List<Member> findMemberCustom();
}


์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค

/**
 * ํด๋ž˜์Šค ์ด๋ฆ„์€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค ์ด๋ฆ„ + Impl๋กœ ์ง€์–ด์•ผ ํ•œ๋‹ค. 
 * ๊ทธ๋Ÿฌ๋ฉด ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค๋กœ ์ธ์‹ํ•œ๋‹ค.
 */
public class MemberRepositoryImpl implements c {
    @Override
    public List<Member> findMemberCustom() {
        // ์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌํ˜„...
    }
}


์‚ฌ์šฉ์ž ์ •์˜ ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์†

public interface MemberRepository
    extends JpaRepository<Member, Long>, MemberRepositoryCustom {}


์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค ์ด๋ฆ„ ๊ทœ์น™ ๋ณ€๊ฒฝ

<jpa:repositories base-package="jpabook.jpashop.repository"
    repository-impl-postfix="Impl" />
@Configuration
@EnableJpaRepositories(
    basePackage = "jpabook.jpashop.repository",
    repositoryImplPostfix = "Impl"
)
public class AppConfig {}



12.7 Web ํ™•์žฅ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ํ”„๋กœ์ ํŠธ๋Š” ์Šคํ”„๋ง MVC์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

12.7.1 ์„ค์ •

<bean class="org.springframework.data.web.config.SpringDataWebConfiguration">
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport // ์ถ”๊ฐ€
public class WebAppConfig {...}


12.7.2 ๋„๋ฉ”์ธ ํด๋ž˜์Šค ์ปจ๋ฒ„ํ„ฐ ๊ธฐ๋Šฅ

๋„๋ฉ”์ธ ํด๋ž˜์Šค ์ปจ๋ฒ„ํ„ฐ๋Š” HTTP ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ ์—”ํ‹ฐํ‹ฐ์˜ ์•„์ด๋””๋กœ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์ฐพ์•„์„œ ๋ฐ”์ธ๋”ฉ ํ•ด์ค€๋‹ค.

// URL: /member/memberUpdateForm?id=1

@Controller
public class MemberController {
    
    @RequestMepping("member/memberUpdateForm")
    public String memberUpdateForm(@RequestParam("id") Member member, Model model) {
        model.addAttribute("member", member);
        return "member/memberSaveForm";
    }
}

โš ์ฃผ์˜
๋„๋ฉ”์ธ ํด๋ž˜์Šค ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ†ตํ•ด ๋„˜์–ด์˜จ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ง์ ‘ ์ˆ˜์ •ํ•ด๋„ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๊ฒƒ์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋™์ž‘ ๋ฐฉ์‹๊ณผ ๊ด€๋ จ์ด ์žˆ๋‹ค. ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋™์ž‘ ๋ฐฉ์‹๊ณผ OSIV์— ๊ด€ํ•œ ๋‚ด์šฉ์„ ์•Œ์•„์•ผ ํ•œ๋‹ค.



12.7.3 ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ ๊ธฐ๋Šฅ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ ๊ธฐ๋Šฅ์„ ์Šคํ”„๋ง MVC์—์„œ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@Controller
public class MemberController {
    
    // ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ Pageable๋ฅผ ๋ฐ›๋Š”๋‹ค
    @RequestMepping("members", method = RequestMethod.GET)
    public String list(Pageable pageable, Model model) {
        Page<Member> page = memberService.findMembers(pageable);
        model.addAttribute("member", page.getContent());
        return "member/memberList";
    }
}
// ์˜ˆ) /members?page=0&size=20&sort=name,desc&sort=address.city

์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ

  • page: ํ˜„์žฌ ํŽ˜์ด์ง€, 0๋ถ€ํ„ฐ ์‹œ์ž‘
  • size: ํ•œ ํŽ˜์ด์ง€์— ๋…ธ์ถœํ•  ๋ฐ์ดํ„ฐ ๊ฑด์ˆ˜
  • sort: ์ •๋ ฌ ์กฐ๊ฑด์„ ์ •์˜

์ฐธ๊ณ 
ํŽ˜์ด์ง€๋ฅผ 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ณ  ์‹ถ์œผ๋ฉด PageableHandlerMethodArgumentResolver๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์ง์ ‘ ๋“ฑ๋กํ•˜๊ณ  setOneIndexedParameters๋ฅผ true๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.


์ ‘๋‘์‚ฌ
์‚ฌ์šฉํ•ด์•ผ ํ•  ํŽ˜์ด์ง• ์ •๋ณด๊ฐ€ ๋‘˜ ์ด์ƒ์ด๋ฉด ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

public String list(
    @Qualifier("member") pageable memberPageable,
    @Qualifier("order") pageable orderPageable.
    ...
)
// ์˜ˆ) /members?member_page=0&order_page=1


๊ธฐ๋ณธ๊ฐ’
Pageable์˜ ๊ธฐ๋ณธ๊ฐ’์€ page=0, size=20์ด๋‹ค. ๋งŒ์•ฝ ๊ธฐ๋ณธ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์œผ๋ฉด @PageableDefault ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

@RequestMepping("members_page", method = RequestMethod.GET)
public String list(@PageableDefault(size = 12, sort = "name", 
    direction = Sort.Direction.DESC) Pageable pageable {
        ...
    }



12.8 ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌํ˜„์ฒด

๋‚ด์šฉ ์ƒ๋žต ๐Ÿ˜…

12.9 JPA ์ƒต์— ์ ์šฉ

๋‚ด์šฉ ์ƒ๋žต ๐Ÿ˜…

12.10 ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์™€ QueryDSL ํ†ตํ•ฉ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ QueryDSL์„ ์ง€์›ํ•œ๋‹ค.

12.10.1 QueryDslPredicateExecutor ์‚ฌ์šฉ

// ๋ ˆํฌ์ง€ํ† ๋ฆฌ์—์„œ QueryDslPredicateExecutor ์ƒ์†
public interface ItemRepository
    extends JpaRepository<Item, Long>, QueryDslPredicateExecutor<Item> {...}
// ์‚ฌ์šฉ
QItem item = QItem.item;
Iterable<Item> result = itemRepository.findAll(
    item.name.contains("์žฅ๋‚œ๊ฐ").and(item.price.between(10000, 20000))
);

โš ๏ธ๊ฒฝ๊ณ 
QueryDslPredicateExecutor๋Š” ๊ธฐ๋Šฅ์˜ ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด join, fetch๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.


12.10.2 QueryDslRepositorySupport ์‚ฌ์šฉ

QueryDSL์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด JPAQuery ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” QueryDslRepositorySupport๋ฅผ ์ƒ์† ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋ฉด ํŽธ๋ฆฌํ•˜๊ฒŒ QueryDSL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์‚ฌ์šฉ์ž ์ •์˜ ๋ ˆํฌ์ง€ํ† ๋ฆฌ
public interface CustomOrderRepository {
    public List<Order> search(OrderSearch orderSearch);
}
public class OrderRepositoryImpl extends QueryDslRepositorySupport
        implements CustomOrderRepository {
    public OrderRepositoryImpl() {
        super(Order.class);
    }

    @Override
    public List<Order> search(OrderSearch orderSearch) {
        QOrder order = QOrder.order;
        QMember member = QMember.member;

        JPAQuery query = from(order);
        // ๊ฒ€์ƒ‰ ์กฐ๊ฑด ์ฟผ๋ฆฌ...
        return query.list(order);
    }
}






13. ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์˜์†์„ฑ ๊ด€๋ฆฌ

์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ JPA๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹์„ ์ดํ•ดํ•˜๊ณ , ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์ ๊ณผ ํ•ด๊ฒฐ๋ฐฉ์•ˆ์„ ์•Œ์•„๋ณธ๋‹ค.

13.1 ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„์˜ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ

์Šคํ”„๋ง์ด๋‚˜ J2EE ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ „๋žต์„ ๋”ฐ๋ผ์•ผ ํ•œ๋‹ค.

13.1.1 ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ ๊ธฐ๋ณธ ์ „๋žต

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„์˜ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ „๋žต์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.


์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณดํ†ต ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‹œ์ž‘ํ•˜๋Š” ์„œ๋น„์Šค ๊ณ„์ธต์— @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์„ ์–ธํ•ด์„œ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•œ๋‹ค.


ํŠธ๋ Œ์žญ์…˜์ด ๊ฐ™์œผ๋ฉด ๊ฐ™์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.


ํŠธ๋žœ์žญ์…˜์ด ๋‹ค๋ฅด๋ฉด ๋‹ค๋ฅธ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.



13.2 ์ค€์˜์† ์ƒํƒœ์™€ ์ง€์—ฐ ๋กœ๋”ฉ

์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์˜ ๊ธฐ๋ณธ ์ „๋žต์ธ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„์˜ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ „๋žต์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์ด ์—†๋Š” ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—์„œ ์—”ํ‹ฐํ‹ฐ๋Š” ์ค€์˜์† ์ƒํƒœ๋‹ค. ๋”ฐ๋ผ์„œ ๋ณ€๊ฒฝ ๊ฐ์ง€์™€ ์ง€์—ฐ ๋กœ๋”ฉ์ด ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ค€์˜์† ์ƒํƒœ์—์„œ ์ง€์—ฐ๋กœ๋”ฉ์„ ์‹œ๋„ํ•˜๋ฉด org.hibernate.LazyInitializationException ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ค€์˜์† ์ƒํƒœ์˜ ์ง€์—ฐ ๋กœ๋”ฉ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  1. ๋ทฐ๊ฐ€ ํ•„์š”ํ•œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋”ฉํ•ด๋‘๋Š” ๋ฐฉ๋ฒ•
    • ๊ธ€๋กœ๋ฒŒ ํŽ˜์น˜ ์ „๋žต ์ˆ˜์ •
    • JPQL ํŽ˜์น˜ ์กฐ์ธ (fetch join)
    • ๊ฐ•์ œ๋กœ ์ดˆ๊ธฐํ™”
  2. OSIV(open session in view)๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์† ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•

13.2.1 ๊ธ€๋กœ๋ฒŒ ํŽ˜์น˜ ์ „๋žต ์ˆ˜์ •

์ฆ‰์‹œ ๋กœ๋”ฉ ์„ค์ •

@ManyToOne(fetch = FetchType.EAGER)

ํ•˜์ง€๋งŒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋กœ๋”ฉํ•˜๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ๋˜ JPQL์—์„œ N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.(ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ)


13.2.2 JPQL ํŽ˜์น˜ ์กฐ์ธ

-- JPQL fetch
select o from Order o join fetch o.member

๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ํ™”๋ฉด์— ๋งž์ถ˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ๊ฐ€ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ตญ ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์ด ์•Œ๊ฒŒ ๋ชจ๋ฅด๊ฒŒ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์„ ์นจ๋ฒ”ํ•˜๊ฒŒ ๋œ๋‹ค.

13.2.3 ๊ฐ•์ œ๋กœ ์ดˆ๊ธฐํ™”

@Transactional
public Order findOrder(id) {
    Order order = orderRepository.findOrder(id);
    order.getMember().getName(); // ํ”„๋ก์‹œ ๊ฐ•์ œ๋กœ ์ดˆ๊ธฐํ™”
    return order;
}

ํ•˜์ง€๋งŒ ํ”„๋ก์‹œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์—ญํ• ์„ ์„œ๋น„์Šค ๊ณ„์ธต์ด ๋‹ด๋‹นํ•˜๋ฉด ๋ทฐ๊ฐ€ ํ•„์š”ํ•œ ์—”ํ‹ฐํ‹ฐ์— ๋”ฐ๋ผ ์„œ๋น„์Šค ๊ณ„์ธต์˜ ๋กœ์ง์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค. ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์ด ์•Œ๊ฒŒ ๋ชจ๋ฅด๊ฒŒ ์„œ๋น„์Šค ๊ณ„์ธต์„ ์นจ๋ฒ”ํ•˜๊ฒŒ ๋œ๋‹ค.

13.2.4 FACADE ๊ณ„์ธต ์ถ”๊ฐ€

FACADE ๊ณ„์ธต์˜ ์—ญํ• ๊ณผ ํŠน์ง•

  • ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๊ณ„์ธต๊ณผ ๋„๋ฉ”์ธ ๋ชจ๋ธ ๊ณ„์ธต ๊ฐ„์˜ ๋…ผ๋ฆฌ์  ์˜์กด์„ฑ์„ ๋ถ„๋ฆฌํ•ด์ค€๋‹ค.
  • ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—์„œ ํ•„์š”ํ•œ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
  • ์„œ๋น„์Šค ๊ณ„์ธต์„ ํ˜ธ์ถœํ•ด์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‹คํ–‰ํ•œ๋‹ค.
  • ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ด์„œ ๋ทฐ๊ฐ€ ์š”๊ตฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐพ๋Š”๋‹ค.
class OrderFacade {
    @Autowired
    OrderService orderService;

    public Order findOrder(id) {
        Order order = orderService.findOrder(id);
        order.getMember().getName(); // ํ”„๋ก์‹œ ๊ฐ•์ œ๋กœ ์ดˆ๊ธฐํ™”
        return order;
    }
}

class OrderService {

    public Order findOrder(id) {
        return orderRepository.findOrder(id);
    }
}

ํ•˜์ง€๋งŒ ์ค‘๊ฐ„์— ๊ณ„์ธต์ด ํ•˜๋‚˜ ๋” ๋ผ์–ด๋“ค๊ฒŒ ๋˜๊ณ , ๊ฒฐ๊ตญ ๋” ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  FACADE์—๋Š” ๋‹จ์ˆœํžˆ ์„œ๋น„์Šค ๊ณ„์ธต์„ ํ˜ธ์ถœ๋งŒ ํ•˜๋Š” ์œ„์ž„ ์ฝ”๋“œ๊ฐ€ ์ƒ๋‹นํžˆ ๋งŽ์„ ๊ฒƒ์ด๋‹ค.

13.2.5 ์ค€์˜์† ์ƒํƒœ์™€ ์ง€์—ฐ ๋กœ๋”ฉ์˜ ๋ฌธ์ œ์ 

๊ฒฐ๊ตญ ๋ชจ๋“  ๋ฌธ์ œ๋Š” ์—”ํ‹ฐํ‹ฐ๊ฐ€ ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—์„œ ์ค€์˜์† ์ƒํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ๋‹ค.

13.3 OSIV

OSIV(Open Session In View)๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ทฐ๊นŒ์ง€ ์—ด์–ด๋‘”๋‹ค๋Š” ๋œป์ด๋‹ค.

์ฐธ๊ณ 
OSIV๋Š” ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์šฉ์–ด๋‹ค. JPA์—์„œ๋Š” OEIV(Open EntityManager In View)๋ผ๊ณ  ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๊ด€๋ก€์ƒ ๋ชจ๋‘ OSIV๋กœ ๋ถ€๋ฅธ๋‹ค.



13.3.1 ๊ณผ๊ฑฐ OSIV: ์š”์ฒญ ๋‹น ํŠธ๋žœ์žญ์…˜

ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค์ž๋งˆ์ž ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋‚˜ ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๊ณ  ์š”์ฒญ์ด ๋๋‚  ๋•Œ ํŠธ๋žœ์žญ์…˜๋„ ๋๋‚ด๋Š” ๊ฒƒ์„ ์š”์ฒญ ๋‹น ํŠธ๋žœ์žญ์…˜ ๋ฐฉ์‹์˜ OSIV๋ผ ํ•œ๋‹ค.


์š”์ฒญ ๋‹น ํŠธ๋žœ์žญ์…˜ ๋ฐฉ์‹์˜ OSIV ๋ฌธ์ œ์ 

  • ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๊ธฐ๋Šฅ์ด ์ž‘๋™ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•ด ๋ฒ„๋ฆฐ๋‹ค.

ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ง‰๋Š” ๋ฐฉ๋ฒ•

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ œ๊ณต
  • ์ฝ๊ธฐ ์ „์šฉ ๋ฉ”์†Œ๋“œ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋„๋ก ์—”ํ‹ฐํ‹ฐ ๋ž˜ํ•‘
  • DTO๋งŒ ๋ฐ˜ํ™˜

ํ•˜์ง€๋งŒ ์ฝ”๋“œ๋Ÿ‰์ด ์ƒ๋‹นํžˆ ์ฆ๊ฐ€ํ•˜๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.


13.3.2 ์Šคํ”„๋ง OSIV: ๋น„์ฆˆ๋‹ˆ์Šค ๊ณ„์ธต ํŠธ๋žœ์žญ์…˜

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” OSIV๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๊ณ„์ธต์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” OSIV๋‹ค.


๋น„์ฆˆ๋‹ˆ์Šค ๊ณ„์ธต ํŠธ๋žœ์žญ์…˜ OSIV ํŠน์ง•

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต๊นŒ์ง€ ์œ ์ง€ํ•œ๋‹ค.
  • ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—๋Š” ํŠธ๋žœ์žญ์…˜์ด ์—†์œผ๋ฏ€๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋‹ค.
  • ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—๋Š” ํŠธ๋žœ์žญ์…˜์ด ์—†์ง€๋งŒ ์ฝ์„ ์ˆ˜๋Š” ์žˆ์–ด์„œ(ํŠธ๋žœ์žญ์…˜ ์—†์ด ์ฝ๊ธฐ) ์ง€์—ฐ ๋กœ๋”ฉ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง OSIV ์ฃผ์˜์‚ฌํ•ญ
ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•œ ์งํ›„์— ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๋Š” ์„œ๋น„์Šค ๊ณ„์ธต์„ ํ˜ธ์ถœํ•˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”Œ๋Ÿฌ์‹œํ•œ๋‹ค. ์ด ๋•Œ ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—์„œ์˜ ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ • ์‚ฌํ•ญ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

class MemberController {
    
    public String viewMember(Long id) {
        Member member = memberService.getMember(id);
        member.setName("๋ณ€๊ฒฝ"); // ๋ณ€๊ฒฝ

        memberService.biz(); // ๋น„์Šค๋‹ˆ์Šค ๋กœ์ง ํ˜ธ์ถœ
        return "view";
    }
}

class MemberService {
    
    @Transactional
    public void biz() {
        // ...๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰
    }
}


ํ•ด๊ฒฐ๋ฐฉ๋ฒ•
๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋จผ์ € ์ˆ˜ํ–‰ํ•œ๋‹ค.

class MemberController {
    
    public String viewMember(Long id) {
        memberService.biz(); // ๋น„์Šค๋‹ˆ์Šค ๋กœ์ง ๋จผ์ € ํ˜ธ์ถœ

        Member member = memberService.getMember(id);
        member.setName("๋ณ€๊ฒฝ"); // ๋ณ€๊ฒฝ์„ ๋‚˜์ค‘์— ์ˆ˜ํ–‰
        return "view";
    }
}



13.3.3 OSIV ์ •๋ฆฌ

์Šคํ”„๋ง OSIV์˜ ํŠน์ง•

  • OSIV๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•ด์„œ ์š”์ฒญ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ฐ™์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ •์€ ํŠธ๋žœ์žญ์…˜์ด ์žˆ๋Š” ๊ณ„์ธต์—์„œ๋งŒ ๋™์ž‘ํ•œ๋‹ค.
  • ํŠธ๋žœ์žญ์…˜์ด ์—†๋Š” ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์€ ์ง€์—ฐ ๋กœ๋”ฉ์„ ํฌํ•จํ•ด์„œ ์กฐํšŒ๋งŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง OSIV์˜ ๋‹จ์ 

  • OSIV๋ฅผ ์ ์šฉํ•˜๋ฉด ๊ฐ™์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์ด ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค. ํŠนํžˆ ๋กค๋ฐฑ ์ฃผ์˜
  • ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ • ํ›„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ •์‚ฌํ•ญ์ด ๋ฐ˜์˜๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์ง€์—ฐ ๋กœ๋”ฉ์— ์˜ํ•œ ์„ฑ๋Šฅ ํŠœ๋‹์‹œ์— ํ™•์ธํ•ด์•ผ ํ•  ๋ถ€๋ถ„์ด ๋„“๋‹ค.

OSIV vs FACADE vs DTO
FACADE๋“  DTO๋“  OSIV๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„๊ตํ•ด์„œ ์ง€๋ฃจํ•œ ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

OSIV๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋งŒ๋Šฅ์€ ์•„๋‹ˆ๋‹ค
๋ณต์žกํ•œ ํ™”๋ฉด์—์„œ๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ํ™”๋ฉด์— ๋งž๋Š” JPQL๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋“ค๋งŒ ์กฐํšŒํ•ด์„œ DTO๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์€ ํ•ด๊ฒฐ์ฑ…์ผ ์ˆ˜ ์žˆ๋‹ค.

OSIV๋Š” ๊ฐ™์€ JVM์„ ๋ฒ—์–ด๋‚œ ์›๊ฒฉ ์ƒํ™ฉ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค
์›๊ฒฉ์ง€์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง€์—ฐ ๋กœ๋”ฉํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์—”ํ‹ฐํ‹ฐ๋Š” ์ƒ๊ฐ๋ณด๋‹ค ์ž์ฃผ ๋ณ€๊ฒฝ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์™ธ๋ถ€ API๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง์ ‘ ๋…ธ์ถœํ•˜๊ธฐ๋ณด๋‹ค๋Š” ์™„์ถฉ ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ๋Š” DTO๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค.


13.4 ๋„ˆ๋ฌด ์—„๊ฒฉํ•œ ๊ณ„์ธต

OSIV๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ํ”„๋ฆฌ์  ํ…Œ์ด์…˜ ๊ณ„์ธต๊นŒ์ง€ ์‚ด์•„์žˆ์œผ๋ฏ€๋กœ ๋ฏธ๋ฆฌ ์ดˆ๊ธฐํ™”ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ๋‹จ์ˆœํ•œ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ด๋„ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.






14. ์ปฌ๋ ‰์…˜๊ณผ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ

์ด ์žฅ์—์„œ ๋‹ค๋ฃฐ ๋‚ด์šฉ

  • ์ปฌ๋ ‰์…˜: ๋‹ค์–‘ํ•œ ์ปฌ๋ ‰์…˜๊ณผ ํŠน์ง•์„ ์„ค๋ช…ํ•œ๋‹ค.
  • ์ปจ๋ฒ„ํ„ฐ: ์—”ํ‹ฐํ‹ฐ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•œ๋‹ค.
  • ๋ฆฌ์Šค๋„ˆ: ์—”ํ‹ฐํ‹ฐ์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„: ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋“ค์„ ์„ ํƒํ•ด์„œ ํ•จ๊ป˜ ์กฐํšŒํ•œ๋‹ค.

14. ์ปฌ๋ ‰์…˜

14.1.1 List + @OrderColumn

List ์ธํ„ฐํŽ˜์ด์Šค์— @OrderColumn์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ˆœ์„œ๊ฐ€ ์žˆ๋Š” ํŠน์ˆ˜ํ•œ ์ปฌ๋ ‰์…˜์œผ๋กœ ์ธ์‹ํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ˆœ์„œ ๊ฐ’์„ ์ €์žฅํ•ด์„œ ์กฐํšŒํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

@Entity
public class Board {
    ...
    @OneToMany(mappedBy = "board")
    @OrderColumn(name = "POSITION") // ์ถ”๊ฐ€
    private List<Comment> comments = new ArrayList<Comment>();
}


@Entity
public class Comment {
    private String comment;

    @ManyToOne
    @JoinColumn(name = "BOARD_ID")
    private Board board;
}

์ˆœ์„œ๊ฐ€ ์žˆ๋Š” ์ปฌ๋ ‰์…˜์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ˆœ์„œ ๊ฐ’๋„ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•œ๋‹ค.


@OrderColumn์˜ ๋‹จ์ 
@OrderColumn์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ์ ๋“ค ๋•Œ๋ฌธ์— ์‹ค๋ฌด์—์„œ ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

  • @OrderColumn์„ Board ์—”ํ‹ฐํ‹ฐ์— ๋งคํ•‘ํ•˜๋ฏ€๋กœ Comment๋Š” POSITION์˜ ๊ฐ’์„ ์•Œ ์ˆ˜ ์—†๋‹ค.
  • Comment๋ฅผ INSERTํ•  ๋•Œ Board.comments์˜ ์œ„์น˜ ๊ฐ’์„ ์‚ฌ์šฉํ•ด์„œ POSITION์˜ ๊ฐ’์„ UPDATE ํ•˜๋Š” SQL์ด ์ถ”๊ฐ€๋กœ ๋ฐœ์ƒํ•œ๋‹ค.
  • List๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์—ฐ๊ด€๋œ ์œ„์น˜ ๊ฐ’์„ ๋‹ค ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋Œ“๊ธ€ 2๋ฅผ ์‚ญ์ œํ•˜๋ฉด ๋Œ“๊ธ€ 3, ๋Œ“๊ธ€ 4์˜ POSITION ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” UPDATE SQL์ด 2๋ฒˆ ์ถ”๊ฐ€๋กœ ๋ฐœ์ƒํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ•์ œ๋กœ ์‚ญ์ œํ•˜๊ณ  ๋‹ค๋ฅธ ๋Œ“๊ธ€ ๋“ค์˜ POSITION ๊ฐ’์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ์ปฌ๋ ‰์…˜์„ ์ˆœํšŒํ•  ๋•Œ NullPointException์ด ๋ฐœ์ƒํ•œ๋‹ค.


14.1.2 @OrderBy

@OrderBy๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ORDER BY์ ˆ์„ ์‚ฌ์šฉํ•ด์„œ ์ปฌ๋ ‰์…˜์„ ์ •๋ ฌํ•œ๋‹ค.

@Entity
public class Team {
    ...
    @OneToMany(mappedBy = "team")
    @OrderBy("username desc, id asc")
    private Set<Member> members = new HashSet<Member>();
    ...
}

์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์‹คํ–‰๋œ SQL๋ฅผ ๋ณด๋ฉด ORDER BY๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค.

SELECT M.* FROM MEMBER M
WHERE M.TEAM_ID = ?
ORDER BY M.MEMBER_NAME DESC, M.ID ASC

์ฐธ๊ณ 
ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” Set์— @OrderBy๋ฅผ ์ ์šฉํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ์กฐํšŒํ•˜๋ฉด ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด LinkedHashSet์„ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•œ๋‹ค.



14.2 @Converter

์ปจ๋ฒ„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

AttributeConverter๋ฅผ ์ด์šฉํ•œ ๋ฐธ๋ฅ˜ ๋งคํ•‘ ์ฒ˜๋ฆฌ ์ฐธ๊ณ 

14.3 ๋ฆฌ์Šค๋„ˆ

JPA ๋ฆฌ์Šค๋„ˆ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋”ฐ๋ฅธ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฆฌ์Šค๋„ˆ ์ฐธ๊ณ 

14.3.1 ์ด๋ฒคํŠธ ์ข…๋ฅ˜

์ด๋ฒคํŠธ ์ข…๋ฅ˜์™€ ๋ฐœ์ƒ ์‹œ์ 



14.3.2 ์ด๋ฒคํŠธ ์ ์šฉ ์œ„์น˜

์ด๋ฒคํŠธ๋Š” ์—”ํ‹ฐํ‹ฐ์—์„œ ์ง์ ‘ ๋ฐ›๊ฑฐ๋‚˜ ๋ณ„๋„์˜ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋กํ•ด์„œ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

์—”ํ‹ฐํ‹ฐ์— ์ง์ ‘ ์ ์šฉ
@PrePersist, @PostLoadโ€ฆ ๋“ฑ์„ ์ด์šฉํ•œ๋‹ค.

@Entity
public class Duck {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @PrePersist
    public void prePersist(){
        System.out.println("prePersist");
    }

    @PostPersist
    public void PostPersist (){
        System.out.println("PostPersist");
    }
    
    @PostLoad
    public void PostLoad(){
        System.out.println("PostLoad");
    }

    @PreRemove
    public void PreRemove (){
        System.out.println("PreRemove");
    }

    @PostRemove
    public void PostRemove(){
        System.out.println("PostRemove");
    }
    
    @PreUpdate
    public void PreUpdate(){
        System.out.println("PreUpdate");
    }
    
    @PostUpdate
    public void PostUpdate (){
        System.out.println("PostUpdate ");
    }    
}



๋ณ„๋„์˜ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
@EntityListeners๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

@Entity
@EntityListeners(DuckListener.class)
public class Duck {...}
public class DuckListener {

    @PrePersist
    // ํŠน์ • ํƒ€์ž…์ด ํ™•์‹คํ•˜๋‹ค๋ฉด ํŠน์ • ํƒ€์ž…์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    public void prePersist(Duck obj){
        System.out.println("prePersist");
    }

    @PostPersist
    public void PostPersist (Duck obj){
        System.out.println("PostPersist");
    }

    @PreRemove
    public void PreRemove (Object obj){
        System.out.println("PreRemove");
    }

    @PostRemove
    public void PostRemove(Object obj){
        System.out.println("PostRemove");
    }
}

๋ฆฌ์Šค๋„ˆ๋Š” ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜ํ™˜ ํƒ€์ž…์€ void๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.


๊ธฐ๋ณธ ๋ฆฌ์Šค๋„ˆ ์‚ฌ์šฉ
๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ์—์„œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ์œ„ํ•ด ๊ธฐ๋ณธ ๋ฆฌ์Šค๋„ˆ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings ...>
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="jpabook.jpashop.domain.test.listener.DefaultListener">
                </entity-listener>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
    
</entity-mappings>



๋” ์„ธ๋ฐ€ํ•œ ์„ค์ •

  • javax.persistence.ExcludeDefaultListeners: ๊ธฐ๋ณธ ๋ฆฌ์Šค๋„ˆ ๋ฌด์‹œ
  • java.persistence.ExcludeSuperclassListners: ์ƒ์œ„ ํด๋ž˜์Šค ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋ฌด์‹œ
@Entity
@EntityListeners(DuckListener.class)
@ExcludeDefaultListeners
@ExcludeSuperclassListners
public class Duck extends BaseEntity {...}

์—ฌ๋Ÿฌ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋กํ–ˆ์„ ๋•Œ ์ด๋ฒคํŠธ ํ˜ธ์ถœ ์ˆœ์„œ

  1. ๊ธฐ๋ณธ ๋ฆฌ์Šค๋„ˆ
  2. ๋ถ€๋ชจ ํด๋ž˜์Šค ๋ฆฌ์Šค๋„ˆ
  3. ๋ฆฌ์Šค๋„ˆ
  4. ์—”ํ‹ฐํ‹ฐ




14.4 ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„

์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ๊ธฐ๋Šฅ์€ ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ์‹œ์ ์— ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋“ค์„ ํ•จ๊ป˜ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

  • ์ •์ ์œผ๋กœ ์ •์˜ํ•˜๋Š” Named ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„
  • ๋™์ ์œผ๋กœ ์ •์˜ํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„

14.4.1 Named ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ (์ •์ )

// ์ฃผ๋ฌธ์„ ์กฐํšŒํ•  ๋•Œ ์—ฐ๊ด€๋œ ํšŒ์›๋„ ํ•จ๊ป˜ ์กฐํšŒํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„
@NamedEntityGraph(
    name = "Order.withMember",
    attributeNodes = {@NamedAttributeNode("member")}
)
@Entity
@Table(name = "ORDERS")
public class Order {
 
    @Id
    @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;
 
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "MEMBER_ID")
    private Member member;  // ์ฃผ๋ฌธ ํšŒ์›
 
    ...
}
  • @NamedEntityGraph: Named ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ์ •์˜ํ•œ๋‹ค.
  • name : ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„์˜ ์ด๋ฆ„์„ ์ •์˜ํ•œ๋‹ค.
  • attributeNodes : ํ•จ๊ป˜ ์กฐํšŒํ•  ์†์„ฑ์„ ์„ ํƒํ•œ๋‹ค. ์ด๋•Œ @NamedAttributeNode๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๊ทธ ๊ฐ’์œผ๋กœ ํ•จ๊ป˜ ์กฐํšŒํ•  ์†์„ฑ์„ ์„ ํƒํ•˜๋ฉด ๋œ๋‹ค.

Order.member๊ฐ€ ์ง€์—ฐ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ์ง€๋งŒ, ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„์—์„œ ํ•จ๊ป˜ ์กฐํšŒํ•  ์†์„ฑ์œผ๋กœ member๋ฅผ ์„ ํƒํ–ˆ์œผ๋ฏ€๋กœ ์ด ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Order๋ฅผ ์กฐํšŒํ•  ๋•Œ ์—ฐ๊ด€๋œ member๋„ ํ•จ๊ป˜ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๋ฅผ ๋‘˜ ์ด์ƒ ์ •์˜ํ•  ๊ฒฝ์šฐ @NamedEntityGraphs ์‚ฌ์šฉํ•œ๋‹ค.


14.4.2 em.find()์—์„œ ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ์‚ฌ์šฉ

// ์ •์˜ํ•œ ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๋ฅผ ์ฐพ๊ณ 
EntityGraph graph = em.getEntityGraph("Order.withMember");

// ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๋Š” JPA์˜ ํžŒํŠธ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ ๋™์ž‘
// ํžŒํŠธ์˜ ๊ฐ’์œผ๋กœ ์ฐพ์•„์˜จ ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ์‚ฌ์šฉ
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

// ์กฐํšŒํ•  ๋•Œ ํžŒํŠธ์ •๋ณด ํฌํ•จ
Order order = em.find(Order.class, orderId, hints);



14.4.3 subgraph

Order -> OrderItem -> Item ๊นŒ์ง€ ํ•จ๊ป˜ ์กฐํšŒ

@NamedEntityGraph(
    name = "Order.withAll",
    attributeNodes = {
        @NamedAttributeNode("member"),
        @NamedAttributeNode(value = "orderItems",subgraph = "orderItems")
    },
    subgraphs = @NamedSubgraph(
        name = "orderItems",
        attributeNodes = {@NamedAttributeNode("item")}
    )
)
@Entity
@Table(name = "ORDERS")
public class Order {
 
    @Id
    @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "MEMBER_ID")
    private Member member;  // ์ฃผ๋ฌธ ํšŒ์›

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<OrderItem>();
    ...
}
@Entity
@Table(name = "ORDER_ITEM")
public class OrderItem {
 
    @Id
    @GeneratedValue
    @Column(name = "ORDER_ITEM_ID")
    private Long id;
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ITEM_ID")
    private Item item;  // ์ฃผ๋ฌธ ์ƒํ’ˆ
    ...
}

Order.All์ด๋ผ๋Š” Named ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ์ •์˜ํ–ˆ๋‹ค. ์ด ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํ•จ๊ป˜ ์กฐํšŒํ•œ๋‹ค.

  • Order -> Member
  • Order -> OrderItem
  • OrderItem -> Item (Order์˜ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ subgraphs ์†์„ฑ์— ์ •์˜)

JPQL์—์„œ ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ์‚ฌ์šฉ

List<Order> resultList = 
    em.createQuery("select o from Order o where o.id = :orderId", Order.class)
    .setParameter("orderId", orderId)
    .setHint("javax.persistence.fetchgraph", em.getEntityGraph("Order.withAll"))
    .getResultList();



14.4.5 ๋™์  ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„

์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๋ฅผ ๋™์ ์œผ๋กœ ๊ตฌ์„ฑํ•˜๋ ค๋ฉด createEntityGraph() ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉํ•œ๋‹ค.

๋™์  ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„

EntityGraph<Order> graph = em.createEntityGraph(Order.class);
graph.addAttributeNodes("member");

Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

Order order = em.find(Order.class, orderId, hints);


๋™์  ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ subgraph

EntityGraph<Order> graph = em.createEntityGraph(Order.class);
graph.addAttributeNodes("member");
Subgraph<OrderItem> orderItems = graph.addSubgraph("orderItems");
orderItems.addAttributeNodes("item");

Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

Order order = em.find(Order.class, orderId, hints);



14.4.6 ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ์ •๋ฆฌ

ROOT์—์„œ ์‹œ์ž‘
์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ํ‹‘ ํ•ญ์ƒ ์กฐํšŒํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ROOT์—์„œ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.

์ด๋ฏธ ๋กœ๋”ฉ๋œ ์—”ํ‹ฐํ‹ฐ
์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ด๋ฏธ ๋กœ๋”ฉ๋˜์–ด ์žˆ์œผ๋ฉด ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.

Order order1 = em.find(Order.class, orderId); // ์ด๋ฏธ ์กฐํšŒ
hints.put("javax.persistence.fetchgraph", em.getEntityGraph("Order.withMember"));

// ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๊ณ  order1๊ณผ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค ๋ฐ˜ํ™˜
Order order2 = em.find(Order.class, orderId);


fetchgraph, loadgraph ์ฐจ์ด

  • javax.persistence.fetchgraph: ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„์— ์„ ํƒํ•œ ์†์„ฑ๋งŒ ํ•จ๊ป˜ ์กฐํšŒ
  • javax.persistence.loadgraph: ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„์— ์„ ํƒํ•œ ์†์„ฑ + FetchType.EAGER๋กœ ์„ค์ •๋œ ์—ฐ๊ด€๊ด€๊ณ„ ์กฐํšŒ


14.5 ์ •๋ฆฌ

  • JPA๊ฐ€ ์ง€์›ํ•˜๋Š” ์ปฌ๋ ‰์…˜์˜ ์ข…๋ฅ˜์™€ ํŠน์ง•
  • ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฆฌ์Šค๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํŽ˜์น˜ ์กฐ์ธ์€ ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ์›ํ•˜๋Š” ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํ•œ ๋ฒˆ์— ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.




15. ๊ณ ๊ธ‰ ์ฃผ์ œ์™€ ์„ฑ๋Šฅ ์ตœ์ ํ™”

15.1 ์˜ˆ์™ธ ์ฒ˜๋ฆฌ

  • ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜๋Š” ๊ฒƒ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ˜์˜์‚ฌํ•ญ๋งŒ ๋กค๋ฐฑํ•˜๋Š” ๊ฒƒ์ด์ง€ ์ˆ˜์ •ํ•œ ์ž๋ฐ” ๊ฐ์ฒด๊นŒ์ง€ ์›์ƒํƒœ๋กœ ๋ณต๊ตฌํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ •์ค‘ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ์ดํ„ฐ๋Š” ๋กค๋ฐฑ๋˜์ง€๋งŒ ๊ฐ์ฒด๋Š” ์ˆ˜์ •๋œ ์ƒํƒœ๋กœ ์—”ํ‹ฐํ‹ฐ ์ปจํ…์ŠคํŠธ์— ๋‚จ์•„์žˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ ๋‹ค์Œ์— ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ํŠธ๋žœ์žญ์…˜๋‹น ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ํŠธ๋žœ์žญ์…˜ ์ข…๋ฃŒ ์‹œ์ ์— ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋„ ์ข…๋ฃŒ๋˜๋ฏ€๋กœ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • OSIV์—์„œ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ฒ”์œ„๋ฅผ ํŠธ๋žœ์žญ์…˜์˜ ๋ฒ”์œ„๋ณด๋‹ค ๋„“๊ฒŒ ์„ค์ •ํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ์‹œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์„œ ๋ฌธ์ œ๋ฅผ ์˜ˆ๋ฐฉํ•œ๋‹ค.


15.2 ์—”ํ‹ฐํ‹ฐ ๋น„๊ต

  • ๋™์ผ์„ฑ ๋น„๊ต๋Š” ๊ฐ™์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๊ด€๋ฆฌ๋ฅผ ๋ฐ›๋Š” ์˜์† ์ƒํƒœ์˜ ์—”ํ‹ฐํ‹ฐ์—๋งŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ ์™ธ์—๋Š” ๋™๋“ฑ์„ฑ ๋น„๊ต๋ฅผ ํ•ด์•ผํ•œ๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋น„๊ตํ•  ๋•Œ๋Š” @Id๋“ฑ๊ณผ ๊ฐ™์€ ๋น„์ฆˆ๋‹ˆ์Šคํ‚ค๋ฅผ ํ™œ์šฉํ•œ ๋™๋“ฑ์„ฑ ๋น„๊ต๋ฅผ ๊ถŒ์žฅํ•œ๋‹ค.


15.3 ํ”„๋ก์‹œ ์‹ฌํ™”

15.3.1 ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ํ”„๋ก์‹œ

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์ž์‹ ์ด ๊ด€๋ฆฌํ•˜๋Š” ์˜์† ์—”ํ‹ฐํ‹ฐ์˜ ๋™์ผ์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค. ๊ทธ๋Ÿผ ํ”„๋ก์‹œ๋กœ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ์˜ ๋™์ผ์„ฑ๋„ ๋ณด์žฅํ• ๊นŒ?

// ํ”„๋ก์‹œ ์กฐํšŒ
Member refMember = em.getReference(Member.class, "member1");
// find ์กฐํšŒ
Member findMember = em.find(Member.class, "member1");
์ถœ๋ ฅ ๊ฒฐ๊ณผ

refMember Type = ํ”„๋ก์‹œ ์—”ํ‹ฐํ‹ฐ
findMember  Type = ํ”„๋ก์‹œ ์—”ํ‹ฐํ‹ฐ

refMember๋Š” ํ”„๋ก์‹œ๊ณ  findMember๋Š” ์›๋ณธ ์—”ํ‹ฐํ‹ฐ์ด๋ฏ€๋กœ ๋‘˜์€ ์„œ๋กœ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค๋กœ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์˜์† ์—”ํ‹ฐํ‹ฐ์˜ ๋™์ผ์„ฑ ๋ณด์žฅ์„ ์œ„ํ•ด ํ”„๋ก์‹œ๋กœ ์กฐํšŒ๋œ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•ด์„œ ๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐพ์œผ๋ฉด ํ”„๋ก์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


// find ์กฐํšŒ
Member findMember = em.find(Member.class, "member1");
// ํ”„๋ก์‹œ ์กฐํšŒ
Member refMember = em.getReference(Member.class, "member1");
์ถœ๋ ฅ ๊ฒฐ๊ณผ

findMember  Type = ์›๋ณธ ์—”ํ‹ฐํ‹ฐ
refMember Type = ์›๋ณธ ์—”ํ‹ฐํ‹ฐ

๋ฐ˜๋Œ€๋กœ ์›๋ณธ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋จผ์ € ์กฐํšŒํ•˜๋ฉด ํ”„๋ก์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ด์œ ๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ refMember๋Š” ์›๋ณธ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

15.3.2 ํ”„๋ก์‹œ ๋น„๊ต ํƒ€์ž…

ํ”„๋ก์‹œ๋Š” ์›๋ณธ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ๋งŒ๋“ค์–ด์ง€๋ฏ€๋กœ ํ”„๋ก์‹œ๋กœ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ์˜ ํƒ€์ž…์„ ๋น„๊ตํ•  ๋•Œ๋Š” == ๋น„๊ต๋ฅผ ํ•˜๋ฉด ์•ˆ๋˜๊ณ  ๋Œ€์‹ ์— instanceof๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

Member refMember = em.getReference(Member.class, "member1");

Assert.assertFalse(Member.class == refMember.getClass()); // false
Assert.assertTrue(refMember instanceof Member); // true


15.3.3 ํ”„๋ก์‹œ ๋™๋“ฑ์„ฑ ๋น„๊ต

ํ”„๋กœ์‹œ ๋™๋“ฑ์„ฑ ๋น„๊ต ์ฃผ์˜์‚ฌํ•ญ

  • ํ”„๋ก์‹œ์˜ ํƒ€์ž… ๋น„๊ต๋Š” == ๋Œ€์‹ ์— instanceof๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ํ”„๋ก์‹œ์˜ ๋งด๋ฒ„๋ณ€์ˆ˜์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋ฉด ์•ˆ๋˜๊ณ  ์ ‘๊ทผ์ž ๋ฉ”์†Œ๋“œ(Getter)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

15.3.4 ์ƒ์†๊ด€๊ณ„์™€ ํ”„๋ก์‹œ

ํ”„๋ก์‹œ๋ฅผ ๋ถ€๋ชจ ํƒ€์ž…์œผ๋กœ ์กฐํšŒํ•˜๋ฉด ๋ถ€๋ชจ ํƒ€์ž…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ”„๋ก์‹œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.

Item proxyItem = em.getReference(Item.class, saveBook.getId());

// ์˜ˆ์™ธ๋ฐœ์ƒ java.lang.ClassCastException
// Book book = (Book) proxyItem;

// ๊ฒ€์ฆ
Assert.assertFalse(proxyItem.getClass() == Book.class);
Assert.assertFalse(proxyItem instanceof Book);
Assert.assertTrue(proxyItem instanceof Item);

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

  • instanceof ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  • ํ•˜์œ„ ํƒ€์ž…์œผ๋กœ ๋‹ค์šด์บ์ŠคํŒ…์„ ํ•  ์ˆ˜ ์—†๋‹ค.


๋ช‡ ๊ฐ€์ง€ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•
1. JPQL๋กœ ๋Œ€์ƒ ์ง์ ‘ ์กฐํšŒ

Book jpqlBook = em.createQuery("select b from Book b where b.id=:bookId", Book.class);
            .setParameter("bookId", item.getId())
            .getSingleResult();

ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

2. ๊ธฐ๋Šฅ์„ ์œ„ํ•œ ๋ณ„๋„์˜ ์ธํ„ฐํŽ˜์ด์Šค ์ œ๊ณต

// ํŠน์ • ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
public interface TitleView {
    String getTitle();
}
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item implements TitleView {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
    
    ...
    // Getter, Setter
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;
    private String actor;
    
    @Override
    public String getTitle() {
        return "[์ œ๋ชฉ:" + getName() + " ๊ฐ๋…:" + director + " ๋ฐฐ์šฐ:" + actor + "]";
    }
}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {
    
    private String author;
    private String isbn;

    @Override
    public String getTitle() {
        return "[์ œ๋ชฉ:" + getName() + " ์ €์ž:" + author + "]";
    }
}
// ํ”„๋ก์‹œ ์ธํ„ฐํŽ˜์ด์Šค ์ œ๊ณต ์‚ฌ์šฉ
@Entity
public class OrderItem {
    @Id @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColmn(name = "ITEM_ID")
    private Item item;

    ...

    public void printItem() {
        System.out.println("TITLE=" + item.getTitle());
    }
}
// Item์˜ ๊ตฌํ˜„์ฒด์— ๋”ฐ๋ผ ๊ฐ๊ฐ ๋‹ค๋ฅธ getTitle() ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
OrderItem orderItem = em.find(OrderItem.class, saveOrderItem.getId());
orderItem.printItem();


3. ๋น„์ง€ํ„ฐ ํŒจํ„ด ์‚ฌ์šฉ
๊ตฌํ˜„ ๋‚ด์šฉ ์ƒ๋žต, ์ฑ… ์ฐธ๊ณ  ๐Ÿ˜…

์žฅ์ 

  • ํ”„๋ก์‹œ์— ๋Œ€ํ•œ ๊ฑฑ์ • ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ์›๋ณธ ์—”ํ‹ฐํ‹ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • instanceof์™€ ํƒ€์ž… ์บ์ŠคํŒ… ์—†์ด ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ๊ฐ์ฒด ๊ตฌ์กฐ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ๊ตฌ์กฐ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ์šด ๋™์ž‘์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹จ์ 

  • ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
  • ๊ฐ์ฒด ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋ชจ๋“  Visitor๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.


15.4 ์„ฑ๋Šฅ ์ตœ์ ํ™”

15.4.1 N+1 ๋ฌธ์ œ

  • ์ฆ‰์‹œ ๋กœ๋”ฉ์€ JPQL์„ ์‹คํ–‰ํ•  ๋•Œ N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ง€์—ฐ ๋กœ๋”ฉ์€ ์—ฐ๊ด€๋œ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•  ๋•Œ N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
1. ํŽ˜์น˜ ์กฐ์ธ ์‚ฌ์šฉ

select distinct m from Member m join fetch m.orders

์ผ๋Œ€๋‹ค ์กฐ์ธ์„ ํ•˜๋ฉด ๊ฒฐ๊ณผ๊ฐ€ ๋Š˜์–ด๋‚˜์„œ ์ค‘๋ณต๋œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ distinct๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

2. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ @BatchSize
ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” @BatchSize์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์ง€์ •ํ•œ size๋งŒํผ SQL์˜ IN์ ˆ์„ ์‚ฌ์šฉํ•ด์„œ ์กฐํšŒํ•œ๋‹ค. ๋งŒ์•ฝ ์กฐํšŒํ•œ ํšŒ์›์ด 10๋ช…์ธ๋ฐ size=5๋กœ ์ง€์ •ํ•˜๋ฉด 2๋ฒˆ์˜ SQL๋งŒ ์ถ”๊ฐ€๋กœ ์‹คํ–‰ํ•œ๋‹ค.

@BatchSize
@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
private List<Order> orders = new ArrayList<Order>();

์ฐธ๊ณ  hibernate.default_batch_fetch_size ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์— ๊ธฐ๋ณธ์œผ๋กœ @BatchSize๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


3. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ @Fetch(FetchMode.SUBSELECT)
ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” @Fetch์–ด๋…ธํ…Œ์ด์…˜์— FetchMode.SUBSELECT๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์—ฐ๊ด€๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์„œ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.

@Fetch(FetchMode.SUBSELECT)
@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
private List<Order> orders = new ArrayList<Order>();
-- JPQL
select m from Member m where m.id > 10

-- ์ฆ‰์‹œ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ์กฐํšŒ ์‹œ์ ์—
-- ์ง€์—ฐ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ์ง€์—ฐ๋กœ๋”ฉ๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์—
-- SQL
SELECT O FROM ORDERS O
    WHERE O.MEMBER_ID IN (
        SELECT
            M.ID
        FROM
            MEMBER M
        WHERE M.ID > 10
    )


์ •๋ฆฌ

  • ์ฆ‰์‹œ ๋กœ๋”ฉ์€ ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ์–ด๋ ต๋‹ค.
  • ์ฆ‰์‹œ ๋กœ๋”ฉ์€ ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ  ์ง€์—ฐ ๋กœ๋”ฉ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.
  • ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—์„œ๋งŒ JPQL ํŽ˜์น˜ ์กฐ์ธ ์‚ฌ์šฉํ•˜์ž.


15.4.2 ์ฝ๊ธฐ ์ „์šฉ ์ฟผ๋ฆฌ์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ์œ„ํ•ด ์Šค๋ƒ…์ƒท ์ธ์Šคํ„ด์Šค๋ฅผ ๋ณด๊ด€ํ•˜๋ฏ€๋กœ ๋” ๋งŽ์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ๋‹จ์ˆœํžˆ ์กฐํšŒ๋งŒ ํ•˜๊ณ , ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค์‹œ ์กฐํšŒํ•  ์ผ๋„ ์—†๊ณ  ์ˆ˜์ •ํ•  ์ผ๋„ ์—†๋‹ค๋ฉด ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šค์นผ๋ผ ํƒ€์ž…์œผ๋กœ ์กฐํšŒ
์Šค์นผ๋ผ ํƒ€์ž…์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ฒฐ๊ณผ๋ฅผ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

select o.id, o.name, o.price from Order p


์ฝ๊ธฐ ์ „์šฉ ์ฟผ๋ฆฌ ํžŒํŠธ ์‚ฌ์šฉ
ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ „์šฉ ํžŒํŠธ์ธ org.hibernate.readOnly๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์Šค๋ƒ…์ƒท์„ ๋ณด๊ด€ํ•˜์ง€ ์•Š๋Š”๋‹ค.

TypedQuery<Order> query = em.createQuery("select o from Order o", Order.class);
query.setHint("org.hibernate.readOnly", true);


์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ ์‚ฌ์šฉ
์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ์ฝ๊ธฐ ์ „์šฉ ๋ชจ๋“œ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์˜ต์…˜์€ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•ด๋„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”Œ๋Ÿฌ์‹œ ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์Šค๋ƒ…์ƒท ๋น„๊ต์™€ ๊ฐ™์€ ๋ฌด๊ฑฐ์šด ๋กœ์ง๋“ค์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋œ๋‹ค.

@Transactional(readOnly = true)


์ถ”์ฒœ ๋ฐฉ๋ฒ•

  • ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ ์‚ฌ์šฉ: ํ”Œ๋Ÿฌ์‹œ๋ฅผ ์ž‘๋™ํ•˜์ง€ ์•Š๋„๋ก ํ•ด์„œ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • ์ฝ๊ธฐ ์ „์šฉ ์—”ํ‹ฐํ‹ฐ ์‚ฌ์šฉ: ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์กฐํšŒํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ


15.4.3 ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋„ˆ๋ฌด ๋งŽ์€ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ €์žฅ๋˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ์ ์ ˆํ•œ ๋‹จ์œ„๋กœ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•œ๋‹ค.
  • 2์ฐจ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด 2์ฐจ ์บ์‹œ์— ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ณด๊ด€ํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

JPA ๋“ฑ๋ก ๋ฐฐ์น˜
๋งŽ์€ ์–‘์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋“ฑ๋กํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๊ณ„์† ์Œ“์ด์ง€ ์•Š๋„๋ก ์ผ์ • ๋‹จ์œ„๋งˆ๋‹ค ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ํ”Œ๋Ÿฌ์‹œํ•˜๊ณ  ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•œ๋‹ค.

EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

for(int i=0; i<100000; ++i){
    Product product = new Product("item"+ i, 10000);
    em.persist(product);

    //100๊ฑด๋งˆ๋‹ค ํ”Œ๋Ÿฌ์‹œ์™€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ดˆ๊ธฐํ™”
    if( i % 100 == 0 ) {
        em.flush();
        em.clear();
    }
}

tx.commit();
em.close();โ€‹


JPA ์ˆ˜์ • ๋ฐฐ์น˜
๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋Š” ์•„์ฃผ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด์„œ ์ˆ˜์ •ํ•œ๋‹ค. ์ด๋•Œ ์ˆ˜๋งŽ์€ ๋ฐ์ดํ„ฐ๋“ค์„ ํ•œ ๋ฒˆ์— ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ ค๋‘˜ ์ˆ˜ ์—†์–ด์„œ ๋‹ค์Œ ๋ฐฉ๋ฒ•์„ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

1. JPA ํŽ˜์ด์ง• ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ

EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction();

tx.begin();
int pageSize = 100;

for (int i = 0; i < 10; ++i) {
    // ์กฐํšŒ
    List < Product > resultList = em.createQuery("select p from Product p", Product.class)
        .setFirstResult(i * pageSize)
        .setMaxResults(pageSize)
        .getResultList();

    //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰ 
    for (Product product: resultList) {
        product.setPrice(product.getPrice + 100);
    }

    // ํ”Œ๋Ÿฌ์‹œ, ์ดˆ๊ธฐํ™”
    em.flush();
    em.clear();
}
tx.commit();
em.close();


2. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ scroll ์‚ฌ์šฉ
JPA๋Š” JDBC ์ปค์„œ(CURSOR)๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์ปค์„œ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์„ธ์…˜(SESSION)์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” scroll์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ JDBC ์ปค์„œ๋ฅผ ์ง€์›ํ•œ๋‹ค.

EntityTransaction tx = em.getTransaction();
Session session = em.unwrap(Session.class);

tx.begin();
ScrollableResults scroll = session.createQuery("select p from Product p")
    .setCacheMode(CacheMode.IGNORE) // 2์ฐจ ์บ์‹œ ๊ธฐ๋Šฅ์„ ๋ˆ๋‹ค.
    .scroll(ScrollMode.FORWARD_ONLY);

int count = 0;
while (scroll.next()) {
    Product p = (Product) scroll.get(0);
    p.setPrice(p.getPrice() + 100);

    count++;
    if (count % 100 == 0) {
        session.flush(); //ํ”Œ๋Ÿฌ์‹œ
        session.clear(); //์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ดˆ๊ธฐํ™”
    }
}
tx.commit();
session.close();


3. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๋ฌด์ƒํƒœ ์„ธ์…˜ ์‚ฌ์šฉ

  • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ๋ฌด์ƒํƒœ ์„ธ์…˜์ด๋ผ๋Š” ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.
  • ๋ฌด์ƒํƒœ ์„ธ์…˜์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์‹ฌ์ง€์–ด 2์ฐจ ์บ์‹œ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ ค๋ฉด ๋ฌด์ƒํƒœ ์„ธ์…˜์ด ์ œ๊ณตํ•˜๋Š” update() ๋ฉ”์†Œ๋“œ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค.
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
ScrollableResults scroll = session.createQuery("select p from Product p").scroll();

while (scroll.next()) {
    Product p = (Product) scroll.get(0);
    p.setPrice(p.getPrice() + 100);
    session.update(p); // ์ง์ ‘ update๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค.
}
tx.commit();
session.close();



15.4.4 SQL ์ฟผ๋ฆฌ ํžŒํŠธ ์‚ฌ์šฉ

JPA๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค SQL ํžŒํŠธ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. SQL ํžŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” SQL ํžŒํŠธ๋Š” JPA ๊ตฌํ˜„์ฒด์—๊ฒŒ ์ œ๊ณตํ•˜๋Š” ํžŒํŠธ๊ฐ€ ์•„๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฒค๋”์—๊ฒŒ ์ œ๊ณตํ•˜๋Š” ํžŒํŠธ๋‹ค.

SQL ์ฟผ๋ฆฌ ํžŒํŠธ ์‚ฌ์šฉ

Session session = em.unwrap(Session.class); // ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ง์ ‘ ์‚ฌ์šฉ

List<Member> list = session.creatQuery("select m from Member m")
            .addQueryHint("FULL (MEMBER)") // SQL ํžŒํŠธ ์ถ”๊ฐ€
            .list();
-- ์‹คํ–‰๋œ SQL์„ ๋ณด๋ฉด ์ถ”๊ฐ€ํ•œ ํžŒํŠธ๊ฐ€ ์žˆ๋‹ค.
select
    /*+ FULL (MEMBER) */ m.id, m.name
from
    Member m



15.4.5 ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ ์„ฑ๋Šฅ ์ตœ์ ํ™”

์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด SQL ๋ฐฐ์น˜๋ฅผ ์ด์šฉํ•œ๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ ๋ฐฐ์น˜๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ๋“ฑ๋ก, ์ˆ˜์ • ์‚ญ์ œํ•  ๋•Œ SQL ๋ฐฐ์น˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค

<property name="hibernate.jdbc.batch_size" value="50">

hibernate.jdbc.batch_size ์†์„ฑ์˜ ์†์„ฑ ๊ฐ’์„ 50์œผ๋กœ ์ฃผ๋ฉด ์ตœ๋Œ€ 50๊ฑด์”ฉ ๋ชจ์•„์„œ SQL ๋ฐฐ์น˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. ํ•˜์ง€๋งŒ SQL ๋ฐฐ์น˜๋Š” ๊ฐ™์€ SQL์ผ ๋•Œ๋งŒ ์œ ํšจํ•˜๋‹ค.

em.persist(ner Member()); // 1
em.persist(ner Member()); // 2
em.persist(ner Member()); // 3
em.persist(ner Member()); // 4
em.persist(ner Child());  // 5, ๋‹ค๋ฅธ ์—ฐ์‚ฐ
em.persist(ner Member()); // 6
em.persist(ner Member()); // 7

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด 1,2,3,4๋ฅผ ๋ชจ์•„์„œ ํ•˜๋‚˜์˜ SQL ๋ฐฐ์น˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  5๋ฅผ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•˜๊ณ  6,7์„ ๋ชจ์•„์„œ ์‹คํ–‰ํ•œ๋‹ค. ์ด 3๋ฒˆ์˜ SQL ๋ฐฐ์น˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.



15.5 ์ •๋ฆฌ

  • JPA์˜ ์˜ˆ์™ธ๋Š” ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ์„ ํ‘œ์‹œํ•˜๋Š” ์˜ˆ์™ธ์™€ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š” ์˜ˆ์™ธ๋กœ ๋‚˜๋ˆˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•˜๋Š” ์˜ˆ์™ธ๋Š” ์‹ฌ๊ฐํ•œ ์˜ˆ์™ธ์ด๋ฏ€๋กœ ํŠธ๋žœ์žญ์…˜์„ ๊ฐ•์ œ๋กœ ์ปค๋ฐ‹ํ•ด๋„ ์ปค๋ฐ‹๋˜์ง€ ์•Š๊ณ  ๋กค๋ฐฑ๋œ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์—”ํ‹ฐํ‹ฐ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ํ‚ค๋ฅผ ์‚ฌ์šฉํ•œ ๋™๋“ฑ์„ฑ ๋น„๊ต๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.
  • ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ๋Š” ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ํ”„๋ก์‹œ์ธ์ง€ ์›๋ณธ ์—”ํ‹ฐํ‹ฐ์ธ์ง€ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ”„๋ก์‹œ๋Š” ๊ธฐ์ˆ ์ ์ธ ํ•œ๊ณ„๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ํ•œ๊ณ„์ ์„ ์ธ์‹ํ•˜๊ณ  ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • JPA๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ N+1 ๋ฌธ์ œ๋ฅผ ๊ฐ€์žฅ ์กฐ์‹ฌํ•ด์•ผ ํ•œ๋‹ค. ์ฃผ๋กœ ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ ํ•ด๊ฒฐํ•œ๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์กฐํšŒํ•˜๋ฉด ์Šค๋ƒ…์ƒท์„ ์œ ์ง€ํ•  ํ•„์š”๊ฐ€ ์—†๊ณ  ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”Œ๋Ÿฌ์‹œํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
  • ๋Œ€๋Ÿ‰์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด ์ ์ ˆํ•œ ์‹œ์ ์— ๊ผญ ํ”Œ๋Ÿฌ์‹œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋„ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•œ๋‹ค.
  • JPA๋Š” SQL ์ฟผ๋ฆฌ ํžŒํŠธ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์ง€๋งŒ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด SQL ์ฟผ๋ฆฌ ํžŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.




16. ํŠธ๋žœ์žญ์…˜๊ณผ ๋ฝ, 2์ฐจ ์บ์‹œ

16.1 ํŠธ๋žœ์žญ์…˜๊ณผ ๋ฝ

ํŠธ๋žœ์žญ์…˜ ๊ธฐ์ดˆ์™€ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋‚™๊ด€์  ๋ฝ๊ณผ ๋น„๊ด€์  ๋ฝ์— ๋Œ€ํ•ด ์•Œ์•„๋ณธ๋‹ค.

16.1.1 ํŠธ๋žœ์žญ์…˜๊ณผ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€

ํŠธ๋žœ์žญ์…˜์€ ACID๋ผ ํ•˜๋Š” ์›์ž์„ฑ(Atomicty), ์ผ๊ด€์„ฑ(Consistency), ๊ฒฉ๋ฆฌ์„ฑ(Isolation), ์ง€์†์„ฑ(Durability)์„ ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.

  • ์›์ž์„ฑ: ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰ํ•œ ์ž‘์—…๋“ค์€ ๋งˆ์น˜ ํ•˜๋‚˜์˜ ์ž‘์—…์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋ชจ๋‘ ์„ฑ๊ณต ํ•˜๋“ ๊ฐ€ ๋ชจ๋‘ ์‹คํŒจํ•ด์•ผ ํ•œ๋‹ค.
  • ์ผ๊ด€์„ฑ: ๋ชจ๋“  ํŠธ๋žœ์žญ์…˜์€ ์ผ๊ด€์„ฑ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ฒฉ๋ฆฌ์„ฑ: ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ํŠธ๋žœ์žญ์…˜๋“ค์ด ์„œ๋กœ์—๊ฒŒ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋„๋ก ๊ฒฉ๋ฆฌํ•œ๋‹ค.
  • ์ง€์†์„ฑ: ํŠธ๋žœ์žญ์…˜์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚ด๋ฉด ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ํ•ญ์ƒ ๊ธฐ๋ก๋˜์–ด์•ผ ํ•œ๋‹ค.

ANSI ํ‘œ์ค€ ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ 4๋‹จ๊ณ„

  • READ UNCOMMITED
    • ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝํ•˜๋Š” ๋‚ด์šฉ์ด commit๊ณผ rollback์—ฌ๋ถ€์— ๊ด€๊ณ„ ์—†์ด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—๊ฒŒ ๋…ธ์ถœ๋œ๋‹ค.
  • READ COMMITTED
    • ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝํ•œ ๋ ˆ์ฝ”๋“œ๋Š” commit์ด ์™„๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋งŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
    • commit ์ „์— ์กฐํšŒ๋ฅผ ์‹œ๋„ํ•œ๋‹ค๋ฉด UNDO์˜์—ญ์— ๋ฐฑ์—…๋œ ๋ ˆ์ฝ”๋“œ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • REPEATABLE READ
    • InnoDB ์—”์ง„์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€.
    • REPEATABLE READ๋ž€ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ๋‚ด๋ถ€์—์„œ ๊ฐ™์€ SELECT ๋ฌธ์€ ํ•ญ์ƒ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ.
    • MySQL InnoDB์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜๋ณ„๋กœ ์‹๋ณ„์ž๋ฅผ ์ฃผ๊ณ  ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ UNDO์˜์—ญ์— ๋ฐฑ์—…ํ•œ๋‹ค.
    • ์ด ๋ฐฑ์—…๋œ ๋ฐ์ดํ„ฐ์™€ ํŠธ๋žœ์žญ์…˜ ์‹๋ณ„์ž๋กœ ๋™์ผ ํŠธ๋žœ์žญ์…˜์—์„œ ๋™์ผ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅํ•œ๋‹ค.
  • SERIALIZABLE
    • ๊ฐ€์žฅ ๋†’์€ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€.
    • ๋ ˆ์ฝ”๋“œ๋ฅผ ์กฐํšŒํ•  ๋•Œ Shared Lock์„ ํš๋“ํ•ด์•ผ๋งŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ์—๋Š” Exclusive Lock์„ ํš๋“ํ•ด์•ผ๋งŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์ฆ‰ ํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ ๋‹ค.
    • ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ์ง€ํ‚ค๋Š” ๋ฉด์€ ๊ฐ€์žฅ ์šฐ์ˆ˜ํ•˜์ง€๋งŒ ๋™์‹œ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ์ด ๋–จ์–ด์ง„๋‹ค.

๊ฒฉ๋ฆฌ ์ˆ˜์ค€์— ๋”ฐ๋ฅธ ๋ฌธ์ œ์ 

  • DIRTY READ
    • ํŠธ๋žœ์žญ์…˜์—์„œ ์ž‘์—…์ด ๋‹ค ๋๋‚˜์ง€ ์•Š์•˜์ง€๋งŒ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ์ž‘์—… ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ DIRTY READ๋ผ๊ณ  ํ•œ๋‹ค.
    • ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋กค๋ฐฑ๋ ์ง€, ์ปค๋ฐ‹๋ ์ง€ ๋ชจ๋ฅด๋Š” ์ƒํ™ฉ์—์„œ ์ž‘์—… ๋‚ด์šฉ์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์— ํฐ ๋ฌธ์ œ๋ฅผ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • NON REPEATABLE READ
    • ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ฐ™์€ SELECT๋ฌธ์œผ๋กœ ์กฐํšŒํ•  ๋•Œ ๋งˆ๋‹ค ๋‹ค๋ฅธ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ด๋ฅผ NON REPEATABLE READ์ƒํƒœ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.
  • PHANTOM READ
    • ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ฐ™์€ SELECT๋ฌธ์œผ๋กœ ์กฐํšŒํ•  ๋•Œ ์ด์ „ SELECT์—์„œ๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋˜ ๊ฐ’์ด ๋‹ค์Œ SELECT์— ์กฐํšŒ๋˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€๊ณผ ๋ฌธ์ œ์  ์ •๋ฆฌ

๊ฒฉ๋ฆฌ ์ˆ˜์ค€ DIRTY READ NON-REPEATABLE READ PHANTOM READ
READ UNCOMMITED O O O
READ COMMITTED ย  O O
REPEATABLE READ ย  ย  O
SERIALIZABLE ย  ย  ย 



16.1.2 ๋‚™๊ด€์  ๋ฝ๊ณผ ๋น„๊ด€์  ๋ฝ ๊ธฐ์ดˆ

JPA๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ READ COMMITED ์ •๋„๋กœ ๊ฐ€์ •ํ•œ๋‹ค. ๋งŒ์•ฝ ์ผ๋ถ€ ๋กœ์ง์— ๋” ๋†’์€ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์ด ํ•„์š”ํ•˜๋ฉด ๋‚™๊ด€์  ๋ฝ๊ณผ ๋น„๊ด€์  ๋ฝ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๋‚™๊ด€์  ๋ฝ
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฝ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฒ„์ „ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค. ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ํŠธ๋žœ์žญ์…˜์˜ ์ถฉ๋Œ์„ ์•Œ ์ˆ˜ ์—†๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๋‹ค.

๋น„๊ด€์  ๋ฝ
ํŠธ๋žœ์žญ์…˜์˜ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์šฐ์„  ๋ฝ์„ ๊ฑธ๊ณ  ๋ณด๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฝ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ select for update ๊ตฌ๋ฌธ์ด ์žˆ๋‹ค.

๋‘ ๋ฒˆ์˜ ๊ฐฑ์‹  ๋ถ„์‹ค ๋ฌธ์ œ
์‚ฌ์šฉ์ž A๊ฐ€ ์ˆ˜์ •ํ•˜๊ณ  ์‚ฌ์šฉ์ž B๊ฐ€ 1์ดˆ ๋’ค์— ์ˆ˜์ • ์š”์ฒญ์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์‚ฌ์šฉ์ž B์˜ ์ˆ˜์ •์‚ฌํ•ญ๋งŒ ๋‚จ๊ฒŒ ๋œ๋‹ค. ์ด๊ฒƒ์„ ๋‘ ๋ฒˆ์˜ ๊ฐฑ์‹  ๋ถ„์‹ค ๋ฌธ์ œ๋ผ ํ•œ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ 3๊ฐ€์ง€ ์„ ํƒ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

  • ๋งˆ์ง€๋ง‰ ์ปค๋ฐ‹๋งŒ ์ธ์ •ํ•˜๊ธฐ: ์‚ฌ์šฉ์ž A์˜ ๋‚ด์šฉ์€ ๋ฌด์‹œํ•˜๊ณ  ๋งˆ์ง€๋ง‰์— ์ปค๋ฐ‹ํ•œ ์‚ฌ์šฉ์ž B์˜ ๋‚ด์šฉ๋งŒ ์ธ์ •ํ•œ๋‹ค.
  • ์ตœ์ดˆ ์ปค๋ฐ‹๋งŒ ์ธ์ •ํ•˜๊ธฐ: ์‚ฌ์šฉ์ž A๊ฐ€ ์ด๋ฏธ ์ˆ˜์ •์„ ์™„๋ฃŒํ–ˆ์œผ๋ฏ€๋กœ ์‚ฌ์šฉ์ž B๊ฐ€ ์ˆ˜์ •์„ ์™„๋ฃŒํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์ถฉ๋Œํ•˜๋Š” ๊ฐฑ์‹  ๋‚ด์šฉ ๋ณ‘ํ•ฉํ•˜๊ธฐ: ์‚ฌ์šฉ์ž A์™€ ์‚ฌ์šฉ์ž B์˜ ์ˆ˜์ •์‚ฌํ•ญ์„ ๋ณ‘ํ•ฉํ•œ๋‹ค.



16.1.3 @Version

JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋‚™๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @Version ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด์„œ ๋ฒ„์ „ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

@Version ์ ์šฉ ๊ฐ€๋Šฅ ํƒ€์ž…

  • Long (long)
  • Integer (int)
  • Shoort (short)
  • Timestamp
@Entity
public class Board {

	@Id
	private String id;
	private String title;

	@Version
	private Integer version;
}

์ด์ œ๋ถ€ํ„ฐ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ๋งˆ๋‹ค ๋ฒ„์ „์ด ํ•˜๋‚˜์”ฉ ์ž๋™์œผ๋กœ ์ฆ๊ฐ€ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ์กฐํšŒ ์‹œ์ ์˜ ๋ฒ„์ „๊ณผ ์ˆ˜์ • ์‹œ์ ์˜ ๋ฒ„์ „์ด ๋‹ค๋ฅด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

// ํŠธ๋žœ์žญ์…˜ 1 ์กฐํšŒ title="์ œ๋ชฉA", version=1
Board board = em.find(Board.class, id);

// ํŠธ๋žœ์žญ์…˜ 2์—์„œ ํ•ด๋‹น ๊ฒŒ์‹œ๋ฌผ์„ ์ˆ˜์ •ํ•ด์„œ title="์ œ๋ชฉC", version=2๋กœ ์ฆ๊ฐ€
board.setTitle("์ œ๋ชฉB"); // ํŠธ๋žœ์žญ์…˜ 1 ๋ฐ์ดํ„ฐ ์ˆ˜์ •

save(board);
tx.commit(); //์˜ˆ์™ธ ๋ฐœ์ƒ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค version=2, ์—”ํ‹ฐํ‹ฐ version=1

ํŠธ๋žœ์žญ์…˜ 1์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๋ชฉ B๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ๋ฒ„์ „๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ˜„์žฌ ๋ฒ„์ „ ์ •๋ณด๊ฐ€ ๋‹ค๋ฅด๋ฏ€๋กœ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ตœ์ดˆ ์ปค๋ฐ‹๋งŒ ์ธ์ •ํ•˜๊ธฐ๊ฐ€ ์ ์šฉ๋œ๋‹ค.

@Version์œผ๋กœ ์ถ”๊ฐ€ํ•œ ๋ฒ„์ „ ๊ด€๋ฆฌ ํ•„๋“œ๋Š” JPA๊ฐ€ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž„์˜๋กœ ์ˆ˜์ •ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค(๋ฒŒํฌ ์—ฐ์‚ฐ ์ œ์™ธ) ๋งŒ์•ฝ ๋ฒ„์ „ ๊ฐ’์„ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€ ํ•˜๋ ค๋ฉด ํŠน๋ณ„ํ•œ ๋ฝ ์˜ต์…˜์„ ์„ ํƒํ•˜๋ฉด ๋œ๋‹ค.

์ฐธ๊ณ 
๋ฒŒํฌ ์—ฐ์‚ฐ์€ ๋ฒ„์ „์„ ๋ฌด์‹œํ•œ๋‹ค. ๋ฒŒํฌ ์—ฐ์‚ฐ์—์„œ ๋ฒ„์ „์„ ์ฆ๊ฐ€ํ•˜๋ ค๋ฉด ๋ฒ„์ „ ํ•„๋“œ๋ฅผ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€์‹œ์ผœ์•ผ ํ•œ๋‹ค.
update Member m set m.name = '๋ณ€๊ฒฝ', m.version = m.version + 1



16.1.4 JPA ๋ฝ ์‚ฌ์šฉ

์ฐธ๊ณ 
JPA๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ถ”์ฒœํ•˜๋Š” ์ „๋žต์€ READ COMMITTED ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ + ๋‚™๊ด€์  ๋ฒ„์ „ ๊ด€๋ฆฌ๋‹ค. (๋‘ ๋ฒˆ์˜ ๊ฐฑ์‹  ๋‚ด์—ญ ๋ถ„์‹ค ๋ฌธ์ œ ์˜ˆ๋ฐฉ)

๋ฝ์€ ๋‹ค์Œ ์œ„์น˜์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • EntityManager.lock(), EntityManager.find(), EntityManager.refresh()
  • Query.setLockMode() (TypeQuery ํฌํ•จ)
  • @NamedQuery

JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฝ ์˜ต์…˜์€ javax.persistence.LockModeType์— ์ •์˜๋˜์–ด ์žˆ๋‹ค.

๋ฝ ๋ชจ๋“œ ํƒ€์ž… ์„ค๋ช…
๋‚™๊ด€์  ๋ฝ OPTIMISTIC ๋‚™๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•œ๋‹ค.
๋‚™๊ด€์  ๋ฝ OPTIMISTIC_FORCE_INCREMENT ๋‚™๊ด€์  ๋ฝ + ๋ฒ„์ „์ •๋ณด๋ฅผ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€ํ•œ๋‹ค.
๋น„๊ด€์  ๋ฝ PESSIMISTIC_READ ๋น„๊ด€์  ๋ฝ, ์ฝ๊ธฐ ๋ฝ์„ ์‚ฌ์šฉํ•œ๋‹ค.
๋น„๊ด€์  ๋ฝ PESSIMISTIC_WRITE ๋น„๊ด€์  ๋ฝ, ์“ฐ๊ธฐ ๋ฝ์„ ์‚ฌ์šฉํ•œ๋‹ค.
๋น„๊ด€์  ๋ฝ PESSIMISTIC_FORCE_INCREMENT ๋น„๊ด€์  ๋ฝ, ๋ฒ„์ „์ •๋ณด๋ฅผ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€ํ•œ๋‹ค.
๊ธฐํƒ€ NONE ๋ฝ์„ ๊ฑธ์ง€ ์•Š๋Š”๋‹ค.
๊ธฐํƒ€ READ JPA1.0 ํ˜ธํ™˜ ๊ธฐ๋Šฅ์ด๋‹ค. OPTIMISTIC๊ณผ ๊ฐ™์œผ๋ฏ€๋กœ OPTIMISTIC์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
๊ธฐํƒ€ WRITE JPA1.0 ํ˜ธํ™˜ ๊ธฐ๋Šฅ์ด๋‹ค. OPTIMISTIC_FORCE_INCREMENT์™€ ๊ฐ™๋‹ค



16.1.5 JPA ๋‚™๊ด€์  ๋ฝ

JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋‚™๊ด€์  ๋ฝ์€ ๋ฒ„์ „์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚™๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ฒ„์ „์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๋‚™๊ด€์  ๋ฝ์€ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๋Š” ์‹œ์ ์— ์ถฉ๋Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๋‹ค. ๋‚™๊ด€์  ๋ฝ์˜ ์˜ต์…˜์— ๋”ฐ๋ฅธ ํšจ๊ณผ๋ฅผ ์•Œ์•„๋ณด์ž.

NONE
๋ฝ ์˜ต์…˜์„ ์ ์šฉํ•˜์ง€ ์•Š์•„๋„ ์—”ํ‹ฐํ‹ฐ์— @Version์ด ์ ์šฉ๋œ ํ•„๋“œ๋งŒ ์žˆ์œผ๋ฉด ๋‚™๊ด€์  ๋ฝ์ด ์ ์šฉ๋œ๋‹ค.

  • ์šฉ๋„: ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์˜ํ•ด ๋ณ€๊ฒฝ(์‚ญ์ œ)๋˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค. ์กฐํšŒ ์‹œ์ ๋ถ€ํ„ฐ ์ˆ˜์ • ์‹œ์ ๊นŒ์ง€๋ฅผ ๋ณด์žฅํ•œ๋‹ค.
  • ๋™์ž‘: ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ๋ฒ„์ „์„ ์ฒดํฌํ•˜๋ฉด์„œ ๋ฒ„์ „์„ ์ฆ๊ฐ€ํ•œ๋‹ค.(UPDATE ์ฟผ๋ฆฌ ์‚ฌ์šฉ). ์ด๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฒ„์ „ ๊ฐ’์ด ํ˜„์žฌ ๋ฒ„์ „์ด ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์ด์ : ๋‘ ๋ฒˆ์˜ ๊ฐฑ์‹  ๋ถ„์‹ค ๋ฌธ์ œ๋ฅผ ์˜ˆ๋ฐฉํ•œ๋‹ค.

OPTIMISTIC
@Version๋งŒ ์ ์šฉํ–ˆ์„ ๋•Œ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ๋ฒ„์ „์„ ์ฒดํฌํ•˜์ง€๋งŒ ์ด ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒ๋งŒ ํ•ด๋„ ๋ฒ„์ „์„ ์ฒดํฌํ•œ๋‹ค. ํ•œ ๋ฒˆ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ์„ ๋ณด์žฅํ•œ๋‹ค.

  • ์šฉ๋„: ์กฐํšŒ ์‹œ์ ๋ถ€ํ„ฐ ํŠธ๋žœ์žญ์…˜์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ์„ ๋ณด์žฅํ•œ๋‹ค.
  • ๋™์ž‘: ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๋•Œ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ์กฐํšŒํ•ด์„œ ํ˜„์žฌ ์—”ํ‹ฐํ‹ฐ์˜ ๋ฒ„์ „๊ณผ ๊ฐ™์€์ง€ ๊ฒ€์ฆํ•œ๋‹ค. ๋งŒ์•ฝ ๊ฐ™์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์ด์ : OPTIMISTIC ์˜ต์…˜์€ DIRTY READ์™€ NON-REPEATABLE READ๋ฅผ ๋ฐฉ์ง€ํ•œ๋‹ค.

OPTIMISTIC_FORCE_INCREMENT
ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๋•Œ UPDATE ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.

  • ์šฉ๋„: ๋…ผ๋ฆฌ์ ์ธ ๋‹จ์œ„์˜ ์—”ํ‹ฐํ‹ฐ ๋ฌถ์Œ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒŒ์‹œ๋ฌผ๊ณผ ์ฒจ๋ถ€ํŒŒ์ผ์ด ์ผ๋Œ€๋‹ค, ๋‹ค๋Œ€์ผ์˜ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„์ด๊ณ  ์ฒจ๋ถ€ํŒŒ์ผ์ด ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด๋‹ค. ๊ฒŒ์‹œ๋ฌผ์„ ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ๋‹จ์ˆœํžˆ ์ฒจ๋ถ€ํŒŒ์ผ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ฒŒ์‹œ๋ฌผ์˜ ๋ฒ„์ „์€ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋•Œ ๊ฒŒ์‹œ๋ฌผ์˜ ๋ฒ„์ „๋„ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€ํ•˜๋ ค๋ฉด OPTIMISTIC_FORCE_INCREMENT๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ๋™์ž‘: ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๋•Œ UPDATE ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ๊ฐ•์ œ๋กœ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค. ์ถ”๊ฐ€๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์ˆ˜์ • ์‹œ ๋ฒ„์ „ UPDATE๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด 2๋ฒˆ์˜ ๋ฒ„์ „ ์ฆ๊ฐ€๊ฐ€ ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด์ : ๊ฐ•์ œ๋กœ ๋ฒ„์ „์„ ์ฆ๊ฐ€ํ•ด์„œ ๋…ผ๋ฆฌ์ ์ธ ๋‹จ์œ„์˜ ์—”ํ‹ฐํ‹ฐ ๋ฌถ์Œ์„ ๋ฒ„์ „ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.



16.1.6 JPA ๋น„๊ด€์  ๋ฝ

JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋น„๊ด€์  ๋ฝ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜ ๋ฝ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ์˜์กดํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ์ฃผ๋กœ SQL ์ฟผ๋ฆฌ์— select for update ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์‹œ์ž‘ํ•˜๊ณ  ๋ฒ„์ „ ์ •๋ณด๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋น„๊ด€์  ๋ฝ์˜ ํŠน์ง•

  • ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์•„๋‹Œ ์Šค์นผ๋ผ ํƒ€์ž…์„ ์กฐํšŒํ•  ๋•Œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ์ฆ‰์‹œ ํŠธ๋žœ์žญ์…˜ ์ถฉ๋Œ์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

PESSIMISTIC_WRITE

  • ์šฉ๋„: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์“ฐ๊ธฐ ๋ฝ์„ ๊ฑด๋‹ค
  • ๋™์ž‘: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค select for update๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฝ์„ ๊ฑด๋‹ค.
  • ์ด์ : NON-REPEATABLE READ๋ฅผ ๋ฐฉ์ง€ํ•œ๋‹ค. ๋ฝ์ด ๊ฑธ๋ฆฐ ๋กœ์šฐ๋Š” ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋‹ค.

PESSIMISTIC_READ
๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜๋ณต ์ฝ๊ธฐ๋งŒ ํ•˜๊ณ  ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š” ์šฉ๋„๋กœ ๋ฝ์„ ๊ฑธ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋Œ€๋ถ€๋ถ„์€ PESSIMISTIC_WRITE๋กœ ๋™์ž‘ํ•œ๋‹ค.

  • MySQL: lock in share mode
  • PostgreSQL: for share

PESSIMISTIC_FORCE_INCREMENT
๋น„๊ด€์  ๋ฝ์ค‘ ์œ ์ผํ•˜๊ฒŒ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” nowait๋ฅผ ์ง€์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•ด์„œ for update nowait ์˜ต์…˜์„ ์ ์šฉํ•œ๋‹ค.

  • ์˜ค๋ผํด: for update nowait
  • PostreSQL: for update nowait
  • nowait๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฉด for update๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค.


16.1.7 ๋น„๊ด€์  ๋ฝ๊ณผ ํƒ€์ž„์•„์›ƒ

๋น„๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฝ์„ ํš๋“ํ•  ๋•Œ๊นŒ์ง€ ํŠธ๋žœ์žญ์…˜์ด ๋Œ€๊ธฐํ•œ๋‹ค. ๋ฌดํ•œ์ • ๊ธฐ๋‹ค๋ฆด ์ˆ˜๋Š” ์—†์œผ๋ฏ€๋กœ ํƒ€์ž„์•„์›ƒ ์‹œ๊ฐ„์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.

Map<String, Object> properties = new HashMap<String, Object>();

// ํƒ€์ž„์•„์›ƒ 10์ดˆ๊นŒ์ง€ ๋Œ€๊ธฐ ์„ค์ •
properties.put("java.persistence.lock.timeout", 10000);

Board board = em.find(Board.class, "boardId", LockModeType.PESSIMISTIC_WRITE, properties);




16.2 2์ฐจ ์บ์‹œ

JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฒ”์œ„์˜ ์บ์‹œ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ณ  ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์™€ EHCACHE๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์‹ค์ œ ์บ์‹œ๋ฅผ ์ ์šฉํ•ด๋ณด์ž.

16.2.1 1์ฐจ ์บ์‹œ์™€ 2์ฐจ ์บ์‹œ

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋กœ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ด์ ์ด ๋งŽ์ง€๋งŒ, ์ผ๋ฐ˜์ ์ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ์€ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๊ณ  ์ข…๋ฃŒํ•  ๋•Œ๊นŒ์ง€๋งŒ 1์ฐจ ์บ์‹œ๊ฐ€ ์œ ํšจํ•˜๋‹ค. OSIV๋ฅผ ์‚ฌ์šฉํ•ด๋„ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋ถ€ํ„ฐ ๋๋‚  ๋•Œ๊นŒ์ง€๋งŒ 1์ฐจ ์บ์‹œ๊ฐ€ ์œ ํšจํ•˜๋‹ค. ๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๋กœ ๋ณด๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ ํšŸ์ˆ˜๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ด์ง€๋Š” ๋ชปํ•œ๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ํฌํ•จํ•œ ๋Œ€๋ถ€๋ถ„์˜ JPA ๊ตฌํ˜„์ฒด๋“ค์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฒ”์œ„์˜ ์บ์‹œ๋ฅผ ์ง€์›ํ•˜๋Š”๋ฐ ์ด๊ฒƒ์„ ๊ณต์œ  ์บ์‹œ ๋˜๋Š” 2์ฐจ ์บ์‹œ๋ผ ํ•œ๋‹ค.


1์ฐจ ์บ์‹œ
1์ฐจ ์บ์‹œ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๋‚ด๋ถ€์— ์žˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋กœ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•˜๋Š” ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋Š” 1์ฐจ ์บ์‹œ์— ์ €์žฅ๋œ๋‹ค. ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๊ฑฐ๋‚˜ ํ”Œ๋Ÿฌ์‹œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด 1์ฐจ ์บ์‹œ์— ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ ๋‚ด์—ญ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋™๊ธฐํ™” ํ•œ๋‹ค.


2์ฐจ ์บ์‹œ
2์ฐจ ์บ์‹œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฒ”์œ„์˜ ์บ์‹œ๋‹ค. ๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ๊นŒ์ง€ ์บ์‹œ๊ฐ€ ์œ ์ง€๋œ๋‹ค. ๋ถ„์‚ฐ ์บ์‹œ๋‚˜ ํด๋Ÿฌ์Šคํ„ฐ๋ง ํ™˜๊ฒฝ์˜ ์บ์‹œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋ณด๋‹ค ๋” ์˜ค๋ž˜ ์œ ์ง€๋  ์ˆ˜๋„ ์žˆ๋‹ค. 2์ฐจ ์บ์‹œ๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ ํšŸ์ˆ˜๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.


2์ฐจ ์บ์‹œ์˜ ํŠน์ง•

  • 2์ฐจ ์บ์‹œ๋Š” ์˜์†์„ฑ ์œ ๋‹› ๋ฒ”์œ„์˜ ์บ์‹œ๋‹ค
  • 2์ฐจ ์บ์‹œ๋Š” ์กฐํšŒํ•œ ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค์–ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • 2์ฐจ ์บ์‹œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์บ์‹œํ•˜์ง€๋งŒ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋‹ค๋ฅด๋ฉด ๊ฐ์ฒด ๋™์ผ์„ฑ(a==b)์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.


16.2.2 JPA 2์ฐจ ์บ์‹œ ๊ธฐ๋Šฅ

์บ์‹œ ๋ชจ๋“œ ์„ค์ •
2์ฐจ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด javax.persistence.Cacheable ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. @Cacheable์€ @Cacheable(true), @Cacheable(false)๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ๊ธฐ๋ณธ๊ฐ’์€ true๋‹ค.

@Cacheable
@Entity
public class Member {

	@Id @GeneratedValue
	private Long id;
	...
}

shared-cache-mode๋ฅผ ์„ค์ •ํ•ด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด(์ •ํ™•ํžˆ๋Š” ์˜์†์„ฑ ์œ ๋‹› ๋‹จ์œ„)์— ์บ์‹œ๋ฅผ ์–ด๋–ป๊ฒŒ ์ ์šฉํ• ์ง€ ์˜ต์…˜์„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.

<!-- ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ ์บ์‹œ ๋ชจ๋“œ ์„ค์ • -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="sharedCacheMode" value="ENABLE_SELECTIVE">
    ...

์บ์‹œ ๋ชจ๋“œ๋Š” javax.persistence.SharedCacheMode์— ์ •์˜๋˜์–ด ์žˆ๋‹ค.

์บ์‹œ ๋ชจ๋“œ ์„ค๋ช…
ALL ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์บ์‹œํ•œ๋‹ค.
NONE ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
ENABLE_SELECTIVE Cacheable(true)๋กœ ์„ค์ •๋œ ์—”ํ‹ฐํ‹ฐ๋งŒ ์บ์‹œ๋ฅผ ์ ์šฉํ•œ๋‹ค.
DISABLE_SELECTIVE ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์บ์‹œํ•˜๋Š”๋ฐ Cacheable(false)๋กœ ๋ช…์‹œ๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ์บ์‹œํ•˜์ง€ ์•Š๋Š”๋‹ค.
UNSPECIFIED JPA ๊ตฌํ˜„์ฒด๊ฐ€ ์ •์˜ํ•œ ์„ค์ •์„ ๋”ฐ๋ฅธ๋‹ค.



16.2.3 ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์™€ EHCACHE ์ ์šฉ

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์ง€์›ํ•˜๋Š” ์บ์‹œ๋Š” ํฌ๊ฒŒ 3๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  • ์—”ํ‹ฐํ‹ฐ ์บ์‹œ: ์—”ํ‹ฐํ‹ฐ ๋‹จ์œ„๋กœ ์บ์‹œํ•œ๋‹ค. ์‹๋ณ„์ž๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ์ปฌ๋ ‰์…˜์ด ์•„๋‹Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋กœ๋”ฉํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ปฌ๋ ‰์…˜ ์บ์‹œ: ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ด€๋œ ์ปฌ๋ ‰์…˜์„ ์บ์‹œํ•œ๋‹ค. ์ปฌ๋ ‰์…˜์ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ด๊ณ  ์žˆ์œผ๋ฉด ์‹๋ณ„์ž ๊ฐ’๋งŒ ์บ์‹œํ•œ๋‹ค(ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ธฐ๋Šฅ)
  • ์ฟผ๋ฆฌ ์บ์‹œ: ์ฟผ๋ฆฌ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉํ•ด์„œ ์บ์‹œํ•œ๋‹ค. ๊ฒฐ๊ณผ๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋ฉด ์‹๋ณ„์ž ๊ฐ’๋งŒ ์บ์‹œํ•œ๋‹ค.(ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ธฐ๋Šฅ)

ํ™˜๊ฒฝ์„ค์ •
๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>4.3.10.Final</version>
</dependency>


์บ์‹œ ์ •์ฑ… ์„ค์ •

<!-- src/main/resources/ehcache.xml -->
<ehcache>
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="1200"
        timeToLiveSeconds="1200"
        diskExpiryThreadIntervalSeconds="1200"
        memoryStoreEvictionPolicy="LRU"/>
</ehcache>


ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์บ์‹œ ์‚ฌ์šฉ ์ •๋ณด๋ฅผ ์ •์˜

# application.yml
spring:
  jpa:
    properties:
      hibernate:
        generate_statistics: true
        format_sql: true

        cache:
          use_second_level_cache: true
          region:
            factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory // EHCACHE

      javax:
        persistence:
          sharedCache:
            mode: ENABLE_SELECTIVE
  • hibernate.cache.use_second_level_cache: 2์ฐจ ์บ์‹œ๋ฅผ ํ™œ์„ฑํ™” ํ•œ๋‹ค. ์—”ํ‹ฐํ‹ฐ ์บ์‹œ์™€ ์ปฌ๋ ‰์…˜ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • hibernate.cache.use_query_cach: ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ํ™œ์„ฑํ™” ํ•œ๋‹ค.
  • hibernate.cache.region.factory_class: 2์ฐจ ์บ์‹œ๋ฅผ ์ฒ˜๋ฆฌํ•  ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•œ๋‹ค.
  • hibernate.generate_statistics: ์ด ์†์„ฑ์„ true๋กœ ์„ค์ •ํ•˜๋ฉด ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ํ†ต๊ณ„์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•ด์ฃผ๋Š”๋ฐ ์บ์‹œ ์ ์šฉ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.(์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ฃผ๋ฏ€๋กœ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค)


@Cache
ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ „์šฉ์ธ @Cache ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์„ธ๋ฐ€ํ•œ ์บ์‹œ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

@Cacheable // ์—”ํ‹ฐํ‹ฐ ์บ์‹œ
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ „์šฉ, ์บ์‹œ์™€ ๊ด€๋ จ๋œ ๋” ์„ธ๋ฐ€ํ•  ์„ค์ • ๊ฐ€๋Šฅ
@Entity
public class ParentMember {

	@Id @GeneratedValue
	private Long id;
	private String name;

	@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // ์ปฌ๋ ‰์…˜๋„ ์บ์‹œ ์ ์šฉ ๊ฐ€๋Šฅ
	@OneToMany(mappedBy = "parentMember", cascade = CascadeType.ALL)
	private List<ChildMember> childMembers;
}

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ @Cache ์†์„ฑ

์†์„ฑ ์„ค๋ช…
usage CacheConcurrencyStrategy๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์บ์‹œ ๋™์‹œ์„ฑ ์ „๋žต์„ ์„ค์ •ํ•œ๋‹ค.
region ์บ์‹œ ์ง€์—ญ ์„ค์ •
include ์—ฐ๊ด€ ๊ฐ์ฒด๋ฅผ ์บ์‹œ์— ํฌํ•จํ• ์ง€ ์„ ํƒํ•œ๋‹ค. all, non-laze ์˜ต์…˜์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.(๊ธฐ๋ณธ๊ฐ’ all)


CacheConcurrencyStrategy ์†์„ฑ

์†์„ฑ ์„ค๋ช…
NONE ์บ์‹œ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.
READ_ONLY ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์„ค์ •ํ•œ๋‹ค. ๋“ฑ๋ก, ์‚ญ์ œ๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ˆ˜์ •์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ์ฐธ๊ณ ๋กœ ์ฝ๊ธฐ ์ „์šฉ์ธ ๋ถˆ๋ณ€ ๊ฐ์ฒด๋Š” ์ˆ˜์ •๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” 2์ฐจ ์บ์‹œ๋ฅผ ์กฐํšŒํ•  ๋•Œ ๊ฐ์ฒด๋ฅผ ๋ณต์‚ฌํ•˜์ง€ ์•Š๊ณ  ์›๋ณธ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
NONSTRICT_READ_WRITE ์—„๊ฒฉํ•˜์ง€ ์•Š์€ ์ฝ๊ณ  ์“ฐ๊ธฐ ์ „๋žต์ด๋‹ค. ๋™์‹œ์— ๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์ด ๊นจ์งˆ ์ˆ˜ ์žˆ๋‹ค. EHCACHE๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌดํšจํ™”ํ•œ๋‹ค.
READ_WRITE ์ฝ๊ธฐ ์“ฐ๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ณ  READ COMMITTED ์ •๋„์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๋ณด์žฅํ•œ๋‹ค.
TRANSACTIONAL ์ปจํ…Œ์ด๋„ˆ ๊ด€๋ฆฌ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ค์ •์— ๋”ฐ๋ผ REPEATABLE READ ์ •๋„์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๋ณด์žฅ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.


์บ์‹œ ๋™์‹œ์„ฑ ์ „๋žต ์ง€์› ์—ฌ๋ถ€

Cache read-only nonstrict-read-write nonstrict-read-write transactional
ConcurrentHashMap yes yes yes ย 
EHCache yes yes yes yes
Infinispan yes ย  ย  yes



์บ์‹œ ์˜์—ญ
์œ„์—์„œ ์บ์‹œ๋ฅผ ์ ์šฉํ•œ ์ฝ”๋“œ(์—”ํ‹ฐํ‹ฐ ์ฝ”๋“œ)๋Š” ๋‹ค์Œ ์บ์‹œ ์˜์—ญ์— ์ €์žฅ๋œ๋‹ค.

  • ์—”ํ‹ฐํ‹ฐ ์บ์‹œ ์˜์—ญ
    • jpabook.jpashop.domain.test.cache.ParentMember
    • ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ [ํŒจํ‚ค์ง€ ๋ช… + ํด๋ž˜์Šค ๋ช…]์„ ์‚ฌ์šฉ
  • ์ปฌ๋ ‰์…˜ ์บ์‹œ ์˜์—ญ
    • jpabook.jpashop.domain.test.cache.ParentMember.childMembers
    • ์—”ํ‹ฐํ‹ฐ ์บ์‹œ ์˜์—ญ ์ด๋ฆ„ + ์บ์‹œํ•œ ์ปฌ๋ ‰์…˜์˜ ํ•„๋“œ ๋ช…

ํ•„์š”ํ•˜๋‹ค๋ฉด @Cache(region = "customRegion", ...) ์ฒ˜๋Ÿผ region ์†์„ฑ์„ ์‚ฌ์šฉํ•ด์„œ ์บ์‹œ ์˜์—ญ์„ ์ง์ ‘ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

์บ์‹œ ์˜์—ญ์ด ์ •ํ•ด์ ธ ์žˆ์œผ๋ฉด ์˜์—ญ๋ณ„๋กœ ์„ธ๋ถ€ ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

<ehcache>
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="1200"
        timeToLiveSeconds="1200"
        diskExpiryThreadIntervalSeconds="1200"
        memoryStoreEvictionPolicy="LRU" />
    
    <!-- ParentMember๋ฅผ 600์ดˆ ๋งˆ๋‹ค ์บ์‹œ์—์„œ ์ œ๊ฑฐ -->
    <cache
        name="jpabook.jpashop.domain.test.cache.ParentMember"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="600"
        timeToLiveSeconds="600"
        overflowToDisk="false" />
</ehcache>



์ฟผ๋ฆฌ ์บ์‹œ

  • ์ฟผ๋ฆฌ ์บ์‹œ๋Š” ์ฟผ๋ฆฌ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉํ•ด์„œ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ์˜์†์„ฑ ์œ ๋‹› ์„ค์ •์— hibernate.cache.use_query_cache ์˜ต์…˜์„ ๊ผญ true๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์ ์šฉํ•˜๋ ค๋Š” ์ฟผ๋ฆฌ๋งˆ๋‹ค org.hibernate.cacheable์„ true๋กœ ์„ค์ •ํ•˜๋Š” ํžŒํŠธ๋ฅผ ์ฃผ๋ฉด ๋œ๋‹ค
// ์ฟผ๋ฆฌ ์บ์‹œ ์‚ฌ์šฉ
em.createQuery("select i from Item i", Item.class)
    .setHint("org.hibernate.cacheable", true)
    .getResultList();
 
// @NamedQuery์— ์ฟผ๋ฆฌ ์บ์‹œ ์ ์šฉ
@Entity
@NamedQuery(
    hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"),
    name = "Member.findByUsername",
    query = "select m.address from Member m where m.name = :username"
)
public class Member {...}



์ฟผ๋ฆฌ ์บ์‹œ ์˜์—ญ
hibernate.cache.use_query_cache ์˜ต์…˜์„ true๋กœ ์„ค์ •ํ•ด์„œ ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ํ™œ์„ฑํ™” ํ•˜๋ฉด ๋‹ค์Œ ๋‘ ์บ์‹œ ์˜์—ญ์ด ์ถ”๊ฐ€๋œ๋‹ค.

  • org.hibernate.cache.internal.StandardQueryCache: ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์ €์žฅํ•˜๋Š” ์˜์—ญ์ด๋‹ค. ์ด๊ณณ์—๋Š” ์ฟผ๋ฆฌ, ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ, ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•œ ์‹œ์ ์˜ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค.
  • org.hibernate.cache.spi.UpdateTimestampsCache: ์ฟผ๋ฆฌ ์บ์‹œ๊ฐ€ ์œ ํšจํ•œ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ฟผ๋ฆฌ ๋Œ€์ƒ ํ…Œ์ด๋ธ”์˜ ๊ฐ€์žฅ ์ตœ๊ทผ ๋ณ€๊ฒฝ(๋“ฑ๋ก, ์ˆ˜์ •, ์‚ญ์ œ) ์‹œ๊ฐ„์„ ์ €์žฅํ•˜๋Š” ์˜์—ญ์ด๋‹ค. ์ด๊ณณ์—๋Š” ํ…Œ์ด๋ธ” ๋ช…๊ณผ ํ•ด๋‹น ํ…Œ์ด๋ธ”์˜ ์ตœ๊ทผ ๋ณ€๊ฒฝ๋œ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค.

์ฟผ๋ฆฌ ์บ์‹œ๋Š” ์บ์‹œํ•œ ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์„ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋กœ ์œ ์ง€ํ•˜๋ ค๊ณ  ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์‹œ๊ฐ„๊ณผ ์ฟผ๋ฆฌ ์บ์‹œ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํ…Œ์ด๋ธ”๋“ค์ด ๊ฐ€์žฅ ์ตœ๊ทผ์— ๋ณ€๊ฒฝ๋œ ์‹œ๊ฐ„์„ ๋น„๊ตํ•œ๋‹ค. ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์ ์šฉํ•˜๊ณ  ๋‚œ ํ›„์— ์ฟผ๋ฆฌ ์บ์‹œ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํ…Œ์ด๋ธ”์— ์กฐ๊ธˆ์ด๋ผ๋„ ๋ณ€๊ฒฝ์ด ์žˆ์œผ๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์™€์„œ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ์บ์‹œํ•œ๋‹ค.

์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ๊ทน์ ์ธ ์„ฑ๋Šฅ ํ–ฅ์ƒ์ด ์žˆ์ง€๋งŒ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ณ€๊ฒฝ์ด ์žˆ๋Š” ํ…Œ์ด๋ธ”์— ์‚ฌ์šฉํ•˜๋ฉด ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์ด ๋” ์ €ํ•˜๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ˆ˜์ •์ด ๊ฑฐ์˜ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š” ํ…Œ์ด๋ธ”์— ์‚ฌ์šฉํ•ด์•ผ ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ฃผ์˜
org.hibernate.cache.spi.UpdateTimestampsCache ์ฟผ๋ฆฌ ์บ์‹œ ์˜์—ญ์€ ๋งŒ๋ฃŒ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค. ํ•ด๋‹น ์˜์—ญ์ด ๋งŒ๋ฃŒ๋˜๋ฉด ๋ชจ๋“  ์ฟผ๋ฆฌ ์บ์‹œ๊ฐ€ ๋ฌดํšจํ™”๋œ๋‹ค. EHCACHE์˜ eternal="true" ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์บ์‹œ์—์„œ ์‚ญ์ œ๋˜์ง€ ์•Š๋Š”๋‹ค.

<cache
    name="org.hibernate.cache.spi.UpdateTimestampsCache"
    maxElementsInMemory="10000"
    eternal="true" />


์ฟผ๋ฆฌ ์บ์‹œ์™€ ์ปฌ๋ ‰์…˜ ์บ์‹œ์˜ ์ฃผ์˜์ 
์—”ํ‹ฐํ‹ฐ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์บ์‹œํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ์บ์‹œํ•˜์ง€๋งŒ ์ฟผ๋ฆฌ ์บ์‹œ์™€ ์ปฌ๋ ‰์…˜ ์บ์‹œ๋Š” ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์˜ ์‹๋ณ„์ž ๊ฐ’๋งŒ ์บ์‹œํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์‹๋ณ„์ž ๊ฐ’์„ ํ•œ๋‚˜์”ฉ ์—”ํ‹ฐํ‹ฐ ์บ์‹œ์— ์กฐํšŒํ•ด์„œ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ฐพ๋Š”๋‹ค.

๋ฌธ์ œ๋Š” ์ฟผ๋ฆฌ ์บ์‹œ๋‚˜ ์ปฌ๋ ‰์…˜ ์บ์‹œ๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ์— ์—”ํ‹ฐํ‹ฐ ์บ์‹œ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์„ฑ๋Šฅ์ƒ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

  1. select m from Member m ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ–ˆ๋Š”๋ฐ ์ฟผ๋ฆฌ ์บ์‹œ๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ๋‹ค. ์กฐํšŒ ๊ฒฐ๊ณผ๋Š” 100๊ฑด์ด๋‹ค.
  2. ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์—๋Š” ์‹๋ณ„์ž๋งŒ ์žˆ๋ฅด๋ฏ€๋กœ ํ•œ ๊ฑด์”ฉ ์—”ํ‹ฐํ‹ฐ ์บ์‹œ ์˜์—ญ์—์„œ ์กฐํšŒํ•œ๋‹ค.
  3. Member ์—”ํ‹ฐํ‹ฐ๋Š” ์—”ํ‹ฐํ‹ฐ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ•œ ๊ฑด์”ฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ๋‹ค.
  4. 100๊ฑด์˜ SQL์ด ์‹คํ–‰๋œ๋‹ค.

๋”ฐ๋ผ์„œ ์ฟผ๋ฆฌ ์บ์‹œ๋‚˜ ์ปฌ๋ ‰์…˜ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒฐ๊ณผ ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ์—๋Š” ๊ผญ ์—”ํ‹ฐํ‹ฐ ์บ์‹œ๋ฅผ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค.



16.3 ์ •๋ฆฌ

  • ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์€ 4๋‹จ๊ณ„๊ฐ€ ์žˆ๋‹ค. ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์ด ๋‚ฎ์„์ˆ˜๋ก ๋™์‹œ์„ฑ์€ ์ฆ๊ฐ€ํ•˜์ง€๋งŒ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์— ๋”ฐ๋ฅธ ๋‹ค์–‘ํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜์ด READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์ด์–ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ์ฝ๊ธฐ(REPEATABLE READ)๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • JPA๋Š” ๋‚™๊ด€์  ๋ฝ๊ณผ ๋น„๊ด€์  ๋ฝ์„ ์ง€์›ํ•œ๋‹ค. ๋‚™๊ด€์  ๋ฝ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ง€์›ํ•˜๋Š” ๋ฝ์ด๊ณ , ๋น„๊ด€์  ๋ฝ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŠธ๋žœ์žญ์…˜ ๋ฝ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ์˜์กดํ•œ๋‹ค.
  • 2์ฐจ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์กฐํšŒ ์„ฑ๋Šฅ ๊ทน์ ์œผ๋กœ ๋Œ์–ด์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋‹ค.





๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ™‡๐Ÿปโ€โ™‚๏ธ

์ž๋ฐ” ORM ํ‘œ์ค€ JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ

ํƒœ๊ทธ: , ,

์นดํ…Œ๊ณ ๋ฆฌ:

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ