<JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ณธ๊ธฐ ๋‹ค์ง€๊ธฐ> ๋‚ด์šฉ ์ •๋ฆฌ

๋ธ”๋กœ๊ทธ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค! ๐Ÿก’ integer.blog

Tacademy ๊ฐ•์˜๋ฅผ ๋“ฃ๊ณ  ๋‚ด์šฉ ์ •๋ฆฌ
๊ฐ•์‚ฌ: ๊น€์˜ํ•œ๋‹˜ (ํ˜„ ์šฐ์•„ํ•œํ˜•์ œ๋“ค ๊ฐœ๋ฐœ์ž)

์ด ๊ฐ•์˜๋Š” ๊น€์˜ํ•œ๋‹˜์˜ ๋‘๊บผ์šด JPA ์ฑ…์„ ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋ณด๊ธฐ ์ „์— ๋ณด๋ฉด ๋”ฑ ์ข‹์„ ๊ฐ•์˜๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
๊ทธ๋Ÿฐ๋ฐ ๊ฐ•์˜ ์‹œ๊ฐ„์ด ๊ธธ๋‹ค๋ณด๋‹ˆ ๋‚ด์šฉ ์ •๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์œผ๋ฉด ๋จธ๋ฆฟ์†์— ๋‚จ์•„์žˆ์งˆ ์•Š์•„์„œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ–ˆ๋‹ค.
ํšŒ์‚ฌ์—์„œ ํŒŒ์ผ๋Ÿฟ ํ”„๋กœ์ ํŠธ ํ•˜๋ฉด์„œ ์ด ์ •๋ฆฌ ๊ธ€์„ ๋ณด๊ณ  ๋˜ ๋ณด๊ณ  ๋˜ ๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.

1. SQL ์ค‘์‹ฌ์ ์ธ ๊ฐœ๋ฐœ์˜ ๋ฌธ์ œ์ 

1.1. ๋ฌดํ•œ ๋ฐ˜๋ณต(CRUD)

์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ private String tel;ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด ๋ชจ๋“  tel๊ณผ ๊ด€๋ จ๋œ sql์„ ๋‹ค ์ˆ˜์ •ํ•ด์•ผ ๋œ๋‹ค.

public class member {
  private String memberId;
  private String name;

  ...
}
INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES
SELECT MEMBER_ID, NAME FROM MEMBER M
UPDATE MEMBER SET ...

1.2. Entity ์‹ ๋ขฐ ๋ฌธ์ œ

์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ Member๊ฐ์ฒด์— Team, Order, Delivery์™€ ์—ฐ๊ด€์„ ๋งบ์–ด๋†จ์„๊ฑฐ๋ผ๋Š” ๋ณด์žฅ์ด ์—†์–ด์„œ ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ์ˆ˜ ์—†๋‹ค.

class MemberService {
  ...
  public void process(String id) {
    Member member = memberDAO.find(id);

    member.getTeam(); // ???
    member.getOrder().getDelivery(); // ???
  }
}

1.3. ๊ณ„์ธตํ˜• ์•„ํ‚คํ…์ณ์—์„œ ์ง„์ •ํ•œ ์˜๋ฏธ์˜ ๊ณ„์ธต ๋ถ„ํ• ์ด ์–ด๋ ต๋‹ค.

์œ„์—์„œ ์ฒ˜๋Ÿผ ๋ฌผ๋ฆฌ์ ์œผ๋กœ๋Š” ๋ถ„ํ• ๋˜์–ด์žˆ์ง€๋งŒ ๋…ผ๋ฆฌ์ ์œผ๋กœ๋Š” ๋ถ„ํ• ๋˜์—ˆ๋Š”์ง€ ๋ชจ๋ฅธ๋‹ค.

1.4. SQL์— ์˜์กด์ ์ธ ๊ฐœ๋ฐœ์„ ํ”ผํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

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

๊ฐ์ฒด vs ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

๊ฐ์ฒด๋ฅผ ์˜๊ตฌ ๋ณด๊ด€ํ•˜๋Š” ๋‹ค์–‘ํ•œ ์ €์žฅ์†Œ๊ฐ€ ์žˆ์ง€๋งŒ ํ˜„์‹ค์ ์ธ ๋Œ€์•ˆ์€ RDB์™€ NoSQL ๋ฟ์ด๋‹ค.
๊ฒฐ๊ตญ ๊ฐ์ฒด๋ฅผ SQL๋กœ ๋ณ€ํ™˜(์ž‘์„ฑ)ํ•˜์—ฌ ์ €์žฅํ•˜๊ฒŒ ๋œ๋‹ค.
๊ฐœ๋ฐœ์ž๊ฐ€ SQL ๋งคํผ์ผ์„ ๋„ˆ๋ฌด ๋งŽ์ด ํ•œ๋‹ค.

MyBatis Generate ๊ฐ™์€ ๊ฒฝ์šฐ์— ๋งŒ๋“ค๊ธฐ๋Š” ํŽธํ•˜์ง€๋งŒ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์ž˜ ์•ˆ๋œ๋‹ค.

2.1. ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ฐจ์ด

  1. ์ƒ์†
  2. ์—ฐ๊ด€๊ด€๊ณ„

    • ๊ฐ์ฒด์˜ ์—ฐ๊ด€๊ด€๊ณ„์—๋Š” ๋ฐฉํ–ฅ์„ฑ์ด ์žˆ๋‹ค.
    • ํ…Œ์ด๋ธ”์˜ ์—ฐ๊ด€๊ด€๊ณ„๋Š” ๋ฐฉํ–ฅ์„ฑ์ด ์—†๋‹ค.
    • ๋•Œ๋ฌธ์— ๋ณดํ†ต์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ์ฒด๋ฅผ ํ…Œ์ด๋ธ”์— ๋งž์ถ”์–ด ๋ชจ๋ธ๋งํ•œ๋‹ค.
    class Member {
        String id;      
        Long teamId;  //TEAM_ID FK ์ปฌ๋Ÿผ ์‚ฌ์šฉ
        String username;
    }
    
    class Team {
        Long id;  //TEAM_ID PK ์‚ฌ์šฉ
        String name;
    }
    • ๊ฐ์ฒด๋‹ค์šด ๋ชจ๋ธ๋ง์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
    • Member์— Team์˜ ๊ฐ’์ด ์•„๋‹ˆ๋ผ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์žˆ๋Š”๊ฒƒ์ด ๋” ๊ฐ์ฒด์ง€ํ–ฅ์ ์ธ ๊ฒƒ์ด๋‹ค.
    class Member {
        String id;
        Team team;
        String username;
            
        Team getTeam() {
        return team;
        }
    }
    
    class Team {
        Long id;
        String name;
    }
    • ๊ฐ์ฒด๋Š” ์ž์œ ๋กญ๊ฒŒ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  3. ๋ฐ์ดํ„ฐ ํƒ€์ž…

  4. ๋ฐ์ดํ„ฐ ์‹๋ณ„ ๋ฐฉ๋ฒ•

๊ฐ์ฒด๋‹ต๊ฒŒ ๋ชจ๋ธ๋งํ• ์ˆ˜๋ก ๋งคํ•‘ ์ž‘์—…๋งŒ ๋Š˜์–ด๋‚œ๋‹ค.
๊ฐ์ฒด๋ฅผ ์ž๋ฐ” ์ปฌ๋ ‰์…˜์— ์ €์žฅํ•˜๋“ฏ์ด DB์— ์ €์žฅํ•  ์ˆ˜๋Š” ์—†์„๊นŒ?

2.2. ORM?

  • Object-relational mapping(๊ฐ์ฒด ๊ด€๊ณ„ ๋งคํ•‘)
  • ๊ฐ์ฒด๋Š” ๊ฐ์ฒด๋Œ€๋กœ ์„ค๊ณ„
  • ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Œ€๋กœ ์„ค๊ณ„
  • ORM ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ค‘๊ฐ„์—์„œ ๋งคํ•‘
  • ๋Œ€์ค‘์ ์ธ ์–ธ์–ด์—๋Š” ๋Œ€๋ถ€๋ถ„ ORM ๊ธฐ์ˆ ์ด ์กด์žฌ

2.3. JPA๋Š” ํ‘œ์ค€ ๋ช…์„ธ

  • JPA๋Š” ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ์Œ
  • JPA ํ‘œ์ค€ ๋ช…์„ธ๋ฅผ ๊ตฌํ˜„ํ•œ 3๊ฐ€์ง€ ๊ตฌํ˜„์ฒด
    • Hibernate
    • EclipseLink
    • DataNucleus
  • ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ๋กœ ์“ฐ๋Š” ๊ฒƒ์€ Hibernate๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.(JPA๋Š” ์ธํ„ฐํŽ˜์ด์Šค)

3. ์‹ค์Šต

public class Main {
  public static void main(String[] args) {
  
  EntityManagerFactory emf = Persistence.CreateEntityManagerFactory("hello");
  // persistence.xml persistence-unit์˜ name์ด hello 
  
  EntityManager em = emf.createEntityManager();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  
  // ์ž‘์—…
  try {
      Member member = new Member();
      member.setId(100L);
      member.setName("ํ•œ์ •์ˆ˜");

      em.persist(member);
      tx.commit();
    
  } catch (Exception e) {
      tx.rollback();
  } finally {
      em.close();
  }
  
  emf.close();

3.1. ์ฃผ์˜์ 

  • EntityManagerFactory๋Š” ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•ด์„œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ ๊ณต์œ 
  • EntityManager๋Š” ์“ฐ๋ ˆ๋“œ ๊ฐ„์— ๊ณต์œ ํ•˜๋ฉด ์•ˆ๋œ๋‹ค. (์‚ฌ์šฉํ•˜๊ณ  ๋ฒ„๋ ค์•ผ ํ•œ๋‹ค.)
  • JPA์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์‹คํ–‰

4. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ

  • DDL์„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ์ ์— ์ž๋™ ์ƒ์„ฑ
  • ํ…Œ์ด๋ธ” ์ค‘์‹ฌ -> ๊ฐ์ฒด ์ค‘์‹ฌ
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฉ์–ธ(dialect)๋ฅผ ํ™œ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋งž๋Š” ์ ์ ˆํ•œ DDL ์ƒ์„ฑ
  • ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉ! (์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋‹ค๋“ฌ์–ด์„œ ์‚ฌ์šฉ)

4.1. ์˜ต์…˜

hibernate.hbm2ddl.auto ์˜ ์˜ต์…˜(value)์„ ์•„๋ž˜์˜ ๊ฒƒ๋“ค๋กœ ์‚ฌ์šฉ

  • create: ๊ธฐ์กด ํ…Œ์ด๋ธ” ์‚ญ์ œ ํ›„ ๋‹ค์‹œ ์ƒ์„ฑ (DROP + CREATE)
  • create-drop: create์™€ ๊ฐ™์œผ๋‚˜ ์ข…๋ฃŒ์‹œ์ ์— ํ…Œ์ด๋ธ” DROP
  • update: ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๋ฐ˜์˜ (์šด์˜ DB์— ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋Œ)
  • validate: entity์™€ table์ด ์ •์ƒ ๋งคํ•‘๋˜์—ˆ๋Š”์ง€๋งŒ ํ™•์ธ
  • none: ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ

5. ๋งคํ•‘ ์–ด๋…ธํ…Œ์ด์…˜

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์–ด๋–ค ์‹์œผ๋กœ ๋งคํ•‘๋ ์ง€์— ๋Œ€ํ•œ ๋งคํ•‘์ •๋ณด

  • @Column
    • @Column(name=“EXAMPLE”) ์˜ต์…˜์„ ์ฃผ๋ฉด ํ•„๋“œ์™€ ๋งคํ•‘ํ•  DB์˜ ์ปฌ๋Ÿผ๋ช…์„ ์ง€์ •ํ•œ๋‹ค.
    • ๊ทธ ์™ธ์— insertable, updatable, nullable, unique, length ๋“ฑ์˜ ์˜ต์…˜์ด ์žˆ๋‹ค.
  • @Temporal
    • ๋‚ ์งœ ํƒ€์ž… ๋งคํ•‘
    • @Temporal(TemporalType.DATE) // ๋‚ ์งœ
    • @Temporal(TemporalType.TIME) // ์‹œ๊ฐ„
    • @Temporal(TemporalType.TIMESTAMP) // ๋‚ ์งœ์™€ ์‹œ๊ฐ„
  • @Enumerated(EnumType.STRING)
    • ๋””ํดํŠธ๋Š” EnumType.ORDINAL์ธ๋ฐ Enum์— ์ •์˜๋œ ์ˆœ์„œ๋ฅผ ์ˆซ์ž๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • ๊ทธ๋Ÿฐ๋ฐ ๋งŒ์•ฝ ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋ชจ๋“ ๊ฒŒ ๊ผฌ์—ฌ๋ฒ„๋ฆฌ๋ฏ€๋กœ ์šด์˜์—์„œ๋Š” ์ ˆ๋Œ€ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
    • EnumType.STRING ์˜ต์…˜์„ ์ฃผ๋ฉด Enum์— ์ •์˜๋œ ๊ธ€์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ€๋ฏ€๋กœ, ์ด ์˜ต์…˜์ด ๊ถŒ์žฅ๋œ๋‹ค.
  • @Lob
    • ์ปจํ…์ธ ์˜ ๊ธธ์ด๊ฐ€ ๋„ˆ๋ฌด ๊ธธ ๊ฒฝ์šฐ์— binaryํŒŒ์ผ๋กœ DB์— ๋„ฃ์„ ๊ฒฝ์šฐ @Lob์„ ์‚ฌ์šฉํ•œ๋‹ค.
    • CLOB๊ณผ BLOB์ด ์žˆ๋‹ค. CLOB์€ Character ํ˜•ํƒœ์˜ ๊ธด ์ปจํ…์ธ ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๊ณ , BLOB์€ Binary ํ˜•ํƒœ์˜ ๊ธด ์ปจํ…์ธ ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
    • @Lob ์–ด๋…ธํ…Œ์ด์…˜์„ String ํƒ€์ž…์— ์“ฐ๋ฉด CLOB์ด ๋˜๊ณ , Byte ํƒ€์ž…์— ์“ฐ๋ฉด BLOB์ด ๋œ๋‹ค.
  • @Transient
    • ์ด ์ปฌ๋Ÿผ์€ ๋งคํ•‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • ์ปฌ๋Ÿผ์„ DB์—๋Š” ์ €์žฅํ•˜์ง€ ์•Š์ง€๋งŒ ๊ฐ์ฒด์—๋Š” ๋‘๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ (ex ์ž„์‹œ flag๊ฐ’)

5.1. ์‹๋ณ„์ž ๋งคํ•‘ ์–ด๋…ธํ…Œ์ด์…˜

  • @Id ์ง์ ‘ ๋งคํ•‘
  • @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์œ„์ž„ (MySQL์˜ AUTO INCREMENT)
  • @Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค ์˜ค๋ธŒ์ ํŠธ ์‚ฌ์šฉ (Oracle)
    • @SequenceGenerator ํ•„์š”
  • @Id @GeneratedValue(strategy = GenerationType.TABLE)
    • ํ‚ค ์ƒ์„ฑ์šฉ ํ…Œ์ด๋ธ” ์‚ฌ์šฉ, ๋ชจ๋“  DB์—์„œ ์‚ฌ์šฉ
    • @TableGenerator ํ•„์š”
  • @Id @GeneratedValue(strategy = GenerationType.AUTO)
    • ๋””ํดํŠธ
    • ๋ฐฉ์–ธ(dialect)์— ๋”ฐ๋ผ ์œ„์˜ ๋ฐฉ๋ฒ• ์ค‘ ์ž๋™ ์ง€์ •

5.2. ๊ถŒ์žฅํ•˜๋Š” ์‹๋ณ„์ž ์ „๋žต

  • ๊ธฐ๋ณธํ‚ค์˜ ์ œ์•ฝ ์กฐ๊ฑด: null ์•„๋‹ˆ๊ณ , ์œ ์ผํ•˜๋ฉฐ, ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ํ•˜์ง€๋งŒ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์€ ์—†๊ธฐ ๋•Œ๋ฌธ์—(์‹ฌ์ง€์–ด ์ฃผ๋ฏผ๋ฒˆํ˜ธ๋„) ๋Œ€์ฒดํ‚ค๋ฅผ ์“ฐ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
  • ๋Œ€์ฒดํ‚ค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ Sequence, Auto Increment, ํ‚ค ์ƒ์„ฑ ํ…Œ์ด๋ธ” ๋“ฑ ๋น„์ฆˆ๋‹ˆ์Šค์™€ ์ „ํ˜€ ๊ด€๊ณ„์—†๋Š” ๊ฒƒ์„ ์“ฐ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • intํƒ€์ž…์€ 10์–ต~20์–ต ์‚ฌ์ด์—์„œ ๋๋‚˜๊ธฐ ๋•Œ๋ฌธ์— Long์„ ์“ฐ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
  • ๊ถŒ์žฅ: Longํƒ€์ž… + ๋Œ€์ฒดํ‚ค + ํ‚ค ์ƒ์„ฑ์ „๋žต ์‚ฌ์šฉ

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

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

6.1.1. ์ฐธ์กฐ ๋Œ€์‹ ์— ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ

@Entity
public class Member {
  
  @Id @GeneratedValue
  private Long id;
  
  @Column
  private String name;
  private int age;
  
  @Column(name = "TEAM_ID")
  private Long teamID;
  ...
}

@Entity
public class Team {
  
  @Id @GeneratedValue
  private Long id;
  private String name;
  ...
}

6.1.2. ์™ธ๋ž˜ํ‚ค ์‹๋ณ„์ž๋ฅผ ์ง์ ‘ ๋‹ค๋ฃธ

// ํŒ€ ์ €์žฅ
Team team = new Team();
team.setName("TeamA");
em.persist(team);

// ํšŒ์› ์ €์žฅ
Member member = new Member();
member.setName("member1");
member.setTeamId(team.getId()); // ์ด ๋ถ€๋ถ„!
em.persist(member);

6.1.3. ์‹๋ณ„์ž๋กœ ๋‹ค์‹œ ์กฐํšŒ.

// ์กฐํšŒ
Member findMember = em.find(Member.class, member.getId());

//Member์™€ Team์ด ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์—†์Œ
Team findTeam = em.find(Team.class, team.getId());

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

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

7. ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘

7.1. ๊ฐ์ฒด์˜ ์ฐธ์กฐ(team)์™€ ํ…Œ์ด๋ธ”์˜ ์™ธ๋ž˜ํ‚ค(TEAM_ID)๋ฅผ ๋งคํ•‘ (=์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘)

@Entity
public class Member {
  
  @Id @GeneratedValue
  private Long id;
  
  @Column
  private String name;
  private int age;
  
  //@Column(name = "TEAM_ID")
  //private Long teamID;
  
  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team;
  
  ...
}

@Entity
public class Team {
  
  @Id @GeneratedValue
  private Long id;
  private String name;
  ...
}

์œ„์˜ ์ฝ”๋“œ์—์„œ ๋งŒ์•ฝ @ManyToOne(fetch = FetchType.LAZY) ๋ฅผ ์ฃผ๋ฉด,
Member ๊ฐ์ฒด๋งŒ ์กฐํšŒํ•˜๊ณ  Team ๊ฐ์ฒด๋Š” ์‹ค์ œ ์‚ฌ์šฉ๋˜๋Š” ์‹œ์ ์— DB๋ฅผ ์กฐํšŒํ•œ๋‹ค. (์ง€์—ฐ ๋กœ๋”ฉ)
๊ทธ๋ ‡๋‹ค๊ณ  Team ๊ฐ์ฒด๊ฐ€ null์ด ๋˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋‚˜๊ธฐ ๋•Œ๋ฌธ์— Team ๊ฐ์ฒด๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด(๊ฐ€์งœ ๊ฐ์ฒด)๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.
๋””ํดํŠธ๋Š” (fetch = FetchType.EAGER)๋กœ ๊ฐ™์ด ์กฐํšŒํ•œ๋‹ค.

๊ถŒ์žฅํ•˜๋Š” ๊ฒƒ์€ LAZY(์ง€์—ฐ ๋กœ๋”ฉ)์ด๋‹ค.
ํ˜„์—…์—์„œ๋Š” ์ „๋ถ€ LAZY๋กœ ๋ฐ”๋ฅด๊ณ , ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—์„œ๋Š” ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ๋Š” ์‹œ์ ์— ์›ํ•˜๋Š” ๊ฒƒ์„ ๋ฏธ๋ฆฌ ์ตœ์ ํ™”ํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•์„ ์“ฐ๊ฒŒ ํ•œ๋‹ค.
์ฆ‰, ์†๋‹จํ•ด์„œ ์ตœ์ ํ™”ํ•˜์ง€ ๋ง์ž๋Š” ๊ฒƒ์ด๋‹ค.

7.2. ์—ฐ๊ด€๊ด€๊ณ„ ์ €์žฅ

// ํŒ€ ์ €์žฅ
Team team = new Team();
team.setName("TeamA");
em.persist(team);

// ํšŒ์› ์ €์žฅ
Member member = new Member();
member.setName("member1");
member.setTeam(team); // ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •, ์ฐธ์กฐ ์ €์žฅ
em.persist(member);

7.3. ์ฐธ์กฐ๋กœ ์—ฐ๊ด€๊ด€๊ณ„ ์กฐํšŒ - ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰

// ์กฐํšŒ
Member findMember = em.find(Member.class, member.getId());

// ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์—ฐ๊ด€๊ด€๊ณ„ ์กฐํšŒ
Team findTeam = findMember.getTeam();

8. ์–‘๋ฐฉํ–ฅ ๋งคํ•‘

8.1. Team ๊ฐ์ฒด์—์„œ๋„ Member ๊ฐ–๋„๋ก

@Entity
public class Team {
  
  @Id @GeneratedValue
  private Long id;
  private String name;
  
  @OneToMany(mappedBy = "team")
  List<Member> members = new ArrayList<Member>();
  ...
}

8.2. ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰

// ์กฐํšŒ
Team findTeam = em.find(Team.class, team.getId());

// ์—ญ๋ฐฉํ–ฅ ์กฐํšŒ
int memberSize = findTeam.getMembers().size();

8.3. ๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ”์ด ๊ด€๊ณ„๋ฅผ ๋งบ๋Š” ์ฐจ์ด

  • ๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„
    • ํšŒ์› -> ํŒ€ ์—ฐ๊ด€๊ด€๊ณ„ 1๊ฐœ (๋‹จ๋ฐฉํ–ฅ)
    • ํŒ€ -> ํšŒ์› ์—ฐ๊ด€๊ด€๊ณ„ 1๊ฐœ (๋‹จ๋ฐฉํ–ฅ)
  • ํ…Œ์ด๋ธ” ์—ฐ๊ด€๊ด€๊ณ„
    • ํšŒ์› <-> ํŒ€ ์—ฐ๊ด€๊ด€๊ณ„ 1๊ฐœ (์–‘๋ฐฉํ–ฅ)

8.4. ๊ฐ์ฒด์˜ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„

  • ๊ฐ์ฒด์˜ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋Š” ์‚ฌ์‹ค ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๊ฐ€ ์•„๋‹ˆ๋ผ ์„œ๋กœ ๋‹ค๋ฅธ ๋‹จ๋ฐฉํ–ฅ ๊ด€๊ณ„ 2๊ฐœ์ด๋‹ค.
  • ๊ฐ์ฒด๋ฅผ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์ฐธ์กฐํ•˜๋ ค๋ฉด ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ 2๊ฐœ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

8.5. ํ…Œ์ด๋ธ”์˜ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„

  • ํ…Œ์ด๋ธ”์€ ์™ธ๋ž˜ํ‚ค ํ•˜๋‚˜๋กœ ๋‘ ํ…Œ์ด๋ธ”์˜ ์—ฐ๊ด€๊ด€๊ณ„ ๊ด€๋ฆฌ
  • MEMBER.TEAM_ID ์™ธ๋ž˜ํ‚ค ํ•˜๋‚˜๋กœ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ€์ง (์–‘์ชฝ์œผ๋กœ ์กฐ์ธํ•  ์ˆ˜ ์žˆ๋‹ค.)
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID

8.6. ๊ฐ์ฒด์˜ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„์—์„œ์˜ ๋ฌธ์ œ์ 

์˜ˆ๋ฅผ ๋“ค์–ด Member ๊ฐ์ฒด์—์„œ Team ๊ฐ์ฒด์˜ ๊ฐ’์„ ๋ณ€๊ฒฝ์‹œํ‚ค๊ฑฐ๋‚˜,
Team ๊ฐ์ฒด์—์„œ members์— member๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋“ฑ์˜ ๋ณ€ํ™”๊ฐ€ ์–‘์ชฝ์—์„œ ์ผ์–ด๋‚œ๋‹ค๋ฉด ์–ด๋А์ชฝ์„ ์‹ ๋ขฐํ•ด์•ผ ํ•˜๋Š”๊ฐ€?

๊ทธ๋ž˜์„œ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋กœ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.
์ฆ‰, ํ•œ ์ชฝ์„ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ๋‚˜๋จธ์ง€ ํ•œ์ชฝ์„ ์กฐํšŒ๋งŒ ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

8.7. ์–‘๋ฐฉํ–ฅ ๋งคํ•‘ ๊ทœ์น™

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

8.8. ๋ˆ„๊ตฌ๋ฅผ ์ฃผ์ธ์œผ๋กœ?

  • ์™ธ๋ž˜ํ‚ค๊ฐ€ ์žˆ๋Š” ๊ณณ์„ ์ฃผ์ธ์œผ๋กœ ์ •ํ•ด๋ผ
  • ๊ถŒ์žฅํ•˜๋Š” ๊ฒƒ์€ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ์„ค๊ณ„๋ฅผ ๋๋‚ด๊ณ  ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ์–‘๋ฐฉํ–ฅ์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์ด ์ƒ๊ธฐ๋ฉด ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ถŒํ•œ๋‹ค.

8.9. ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์‹œ ๊ฐ€์žฅ ๋งŽ์ดํ•˜๋Š” ์‹ค์ˆ˜

  • ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์— ๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€ ์•Š๋Š” ๊ฒƒ.
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");

//์—ญ๋ฐฉํ–ฅ(์ฃผ์ธ์ด ์•„๋‹Œ ๋ฐฉํ–ฅ)๋งŒ ์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •
team.getMembers().add(member);
em.persist(member);

์ด ๊ฒฝ์šฐ, TEAM_ID๊ฐ€ null์ด ๋œ๋‹ค.

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

8.10. ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์˜ ์žฅ์ 

  • ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘๋งŒ์œผ๋กœ๋„ ์ด๋ฏธ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘์€ ์™„๋ฃŒ
  • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์€ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ์กฐํšŒ(๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰) ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋œ ๊ฒƒ ๋ฟ
  • JPQL์—์„œ ์—ญ๋ฐฉํ–ฅ์œผ๋กœ ํƒ์ƒ‰ํ•  ์ผ์ด ๋งŽ์Œ
  • ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์„ ์ž˜ํ•˜๊ณ  ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์€ ํ•„์š”ํ•  ๋•Œ ์ถ”๊ฐ€ํ•˜๋ฉด ๋Œ. (ํ…Œ์ด๋ธ”์— ์˜ํ–ฅ ์—†์Œ)

9. JPA ๋‚ด๋ถ€ ๊ตฌ์กฐ - ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ

JPA์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ 2๊ฐ€์ง€

  • ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งคํ•‘ํ•˜๊ธฐ
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ(PersistenceContext)

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

  • JPA๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์šฉ์–ด
  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ์ด๋ผ๋Š” ๋œป
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ๋…ผ๋ฆฌ์ ์ธ ๊ฐœ๋…(๋ˆˆ์— ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.)
  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผ
  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €์™€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” 1:1 ์ด๋ผ์„œ ๊ทธ๋ƒฅ EntityManager = PersistenceContext๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.
    • ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐ™์€ ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ๋Š” EntityManager์™€ PersistenceContext๊ฐ€ N:1์ด๋‹ค.
  • ๊ฐ™์€ ํŠธ๋žœ์žญ์…˜์ด๋ฉด ๊ฐ™์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผํ•˜๊ฒŒ ๋œ๋‹ค.

9.2. Entity์˜ ์ƒ๋ช…์ฃผ๊ธฐ

  • ๋น„์˜์†(new/transient)
    • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒํƒœ
    • Member ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ๋งŒ ํ•œ ์ƒํƒœ
  //๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ์ƒํƒœ(๋น„์˜์†)
  Member member = new Member();
  member.setId("memberId");
  member.setUsername("ํšŒ์›1");
  • ์˜์†(managed)
    • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋œ ์ƒํƒœ
    • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ €์žฅํ•œ ์ƒํƒœ(์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์˜ํ•ด์„œ ๊ฐ์ฒด๊ฐ€ ๊ด€๋ฆฌ(managed)๋˜๋Š” ์ƒํƒœ)
  Member member = new Member();
  member.setId("memberId");
  member.setUsername("ํšŒ์›1");
  
  EntityManager em = emf.createEntityManager();
  em.getTransaction().begin();
  
  //๊ฐ์ฒด๋ฅผ ์ €์žฅํ•œ ์ƒํƒœ(์˜์†)
  em.persist(member);
  • ์ค€์˜์†(detached)
    • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋˜์—ˆ๋‹ค๊ฐ€ ๋ถ„๋ฆฌ๋œ ์ƒํƒœ
    • em.detach(member);
  • ์‚ญ์ œ(removed)
    • ์‚ญ์ œ๋œ ์ƒํƒœ
    • em.remove(member);

10. ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์ด์ 

10.1. 1์ฐจ ์บ์‹œ

  • PersistenceContext์—๋Š” ๋‚ด๋ถ€์— 1์ฐจ ์บ์‹œ๊ฐ€ ์žˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์บ์‹œ๊ฐ€ ์•„๋‹ˆ๋ผ ์˜์†์„ฑ์ปจํ…์Šค๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ์—†์–ด์งˆ ๋•Œ ๊นŒ์ง€๋งŒ ์ž ๊น ์กด์žฌํ•˜๋Š” ๊ฒƒ.
  • 1์ฐจ ์บ์‹œ์—์„œ ์กฐํšŒ
Member member = new Member();
member.setId("member1");
member.setUsername("ํšŒ์›1");

//1์ฐจ ์บ์‹œ์— ์ €์žฅ๋Œ
em.persist(member);

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

em.find()์—์„œ DB๋กœ ๋ฐ”๋กœ๊ฐ€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ 1์ฐจ ์บ์‹œ๋ฅผ ๋จผ์ € ํƒ์ƒ‰ํ•œ๋‹ค. (์กด์žฌํ•˜๋ฉด ๋ฐ”๋กœ ๋ฐ˜ํ™˜)

  • 1์ฐจ ์บ์‹œ์— ์—†์œผ๋ฉด
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•˜๊ณ 
    • ์กฐํšŒ๋œ ๋‚ด์šฉ์„ 1์ฐจ ์บ์‹œ์— ์ €์žฅ ํ›„์— ๋ฐ˜ํ™˜

10.2. ์˜์† ์—”ํ‹ฐํ‹ฐ์˜ ๋™์ผ์„ฑ(identity) ๋ณด์žฅ

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

System.out.println(a == b); // ๋™์ผ์„ฑ ๋น„๊ต true -> ์œ„์—์„œ ๋ณด์•˜๋“ฏ์ด 1์ฐจ์บ์‹œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—
  • 1์ฐจ ์บ์‹œ๋กœ ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ์ฝ๊ธฐ(REPEATABLE READ) ๋“ฑ๊ธ‰์˜ ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์•„๋‹Œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฐจ์›์—์„œ ์ œ๊ณต

10.3. ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ(transactional write-behind) - ๋ฒ„ํผ ๊ธฐ๋Šฅ

  • ์˜ˆ๋ฅผ ๋“ค์–ด persist(memberA) ๋ช…๋ น์˜ ๊ฒฝ์šฐ memberA๋ฅผ 1์ฐจ ์บ์‹œ์— ์ €์žฅํ•˜๋ฉด์„œ ๋™์‹œ์— INSERT SQL์„ ์ƒ์„ฑํ•ด์„œ ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ๋ง์•„๋†“๋Š”๋‹ค.
  • ์ดํ›„์— persist(memberB) ๋ช…๋ น์˜ ๊ฒฝ์šฐ๋„ ์œ„์™€ ๊ฐ™์ด ๋™์ž‘ํ•˜๊ณ , ์—ฌ์ „ํžˆ DB์— ๋„ฃ์ง€ ์•Š๋Š”๋‹ค.
  • ์ดํ›„์— commit() ๋ช…๋ น์„ ํ•ด์•ผ ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ์žˆ๋˜ INSERT SQL ์ฟผ๋ฆฌ 2๊ฐœ๋ฅผ (์˜ต์…˜์— ๋”ฐ๋ผ ๋™์‹œ์— ํ˜น์€ ํ•˜๋‚˜์”ฉ) DB์— ๋„ฃ๋Š”๋‹ค.
  • ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ์žˆ๋˜ ์ฟผ๋ฆฌ๋“ค์„ ๋‚ ๋ฆฌ๋Š” ๊ณผ์ •์„ flush๋ผ๊ณ  ํ•œ๋‹ค.
    • ํ•˜์ง€๋งŒ flush๋ฅผ ํ•œ๋‹ค๊ณ  1์ฐจ์บ์‹œ์˜ ๋‚ด์šฉ๋“ค์ด ์ง€์›Œ์ง€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋‚ด์„œ DB์™€ ์‹ฑํฌ๋ฅผ ๋งž์ถ”๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
    • commit() ์ด flush์™€ commit ๋‘ ๊ฐ€์ง€ ์ผ์„ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์‹œ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
transaction.begin();

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

//์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— INSERT SQL์„ ๋ณด๋‚ธ๋‹ค.
transaction.commit(); // [ํŠธ๋žœ์žญ์…˜] ์ปค๋ฐ‹

10.4. ๋ณ€๊ฒฝ ๊ฐ์ง€(Dirty Checking)

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

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

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

// ์ˆ˜์ •ํ–ˆ์œผ๋‹ˆ em.update(member) ์ด๋Ÿฐ ์ฝ”๋“œ๊ฐ€ ์žˆ์–ด์•ผ ํ•˜์ง€ ์•Š์„๊นŒ?

// ํ•˜์ง€๋งŒ ํ•„์š”์—†๋‹ค. ์ปค๋ฐ‹ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ„๋‹ค.
transaction.commit(); // [ํŠธ๋žœ์žญ์…˜] ์ปค๋ฐ‹
  • Dirty Checking์˜ ๋™์ž‘ ์›๋ฆฌ
    • JPA๋Š” ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋˜๋Š” ์‹œ์ ์— 1์ฐจ ์บ์‹œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์Šค๋ƒ…์ƒท๋„ ์ƒ์„ฑํ•œ๋‹ค.
    • commit()๋ช…๋ น์œผ๋กœ flush๋ฅผ ํ•˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋Š” ์—”ํ‹ฐํ‹ฐ๋“ค์„ ์Šค๋ƒ…์ƒท๊ณผ ๋น„๊ตํ•ด์„œ ๋ฐ”๋€ ๋ถ€๋ถ„์ด ์žˆ์œผ๋ฉด UPDATE ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์„œ DB์— ๋ณด๋‚ด๊ณ  commit์„ ํ•œ๋‹ค.
  • ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ์ด์œ 
    • Java ์ปฌ๋ ‰์…˜์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ ๋ณ€๊ฒฝํ•ด๋„ ๋‹ค์‹œ ์ปฌ๋ ‰์…˜์— ๊ฐ’์„ ๋‹ด์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜๋„ ์ปฌ๋ ‰์…˜์˜ ๊ฐ’์ด ๋ฐ”๋€๋‹ค. ๊ทธ๊ฒƒ๊ณผ ๋˜‘๊ฐ™์€ ์ปจ์…‰์ด๋‹ค.
    • ๋งˆ์น˜ Java ์ปฌ๋ ‰์…˜์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผํ•˜๊ธฐ ์œ„ํ•ด ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

10.5. ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)

11. Flush

11.1. ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ flush ํ•˜๋Š” ๋ฐฉ๋ฒ•

  • em.flush() - ์ง์ ‘ ํ˜ธ์ถœ
  • ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ - ํ”Œ๋Ÿฌ์‹œ ์ž๋™ ํ˜ธ์ถœ
  • JPQL ์ฟผ๋ฆฌ ์‹คํ–‰ - ํ”Œ๋Ÿฌ์‹œ ์ž๋™ ํ˜ธ์ถœ

    • JPQL ์ฟผ๋ฆฌ ์‹คํ–‰์‹œ flush๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ์ด์œ 
    em.persist(memberA);
    em.persist(memberB);
    em.persist(memberC);
        
    // ์ค‘๊ฐ„์— JPQL ์‹คํ–‰  
    query = em.createQuery("select m from Member m", Member.class);
    List<Member> members = query.getResultList();
    • ์ด ์ƒํ™ฉ์—์„œ๋Š” DB์—์„œ ๋ฐ์ดํ„ฐ ์กฐํšŒ๊ฐ€ ํ•˜๋‚˜๋„ ์•ˆ๋œ๋‹ค. flush๊ฐ€ ์•ˆ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—.
    • ๋•Œ๋ฌธ์— JPA์—์„œ๋Š” JPQL์„ ์‹คํ–‰ํ•˜๋ฉด flush๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋˜๋„๋ก ํ–ˆ๋‹ค. (MyBatis๋‚˜ Spring JDBC์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ๋Š” flush๋ฅผ ์ง์ ‘ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.)

11.2. Flush ์˜ต์…˜

  • em.setFlushMode(FlushModeType.AUTO) - ๋””ํดํŠธ
    • ์ปค๋ฐ‹์ด๋‚˜ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ flush
  • em.setFlushMode(FlushModeType.COMMIT)
    • ์ปค๋ฐ‹ํ•  ๋•Œ๋งŒ flush

11.3. Flush๋Š”!

  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋น„์šฐ์ง€ ์•Š์Œ
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ๋ณ€๊ฒฝ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋™๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์ด flush์˜ ๋ชฉ์ 
  • flush๊ฐ€ ๊ฐ€๋Šฅํ•œ ์ด์œ ๋Š” DB์— ํŠธ๋žœ์žญ์…˜์ด๋ผ๋Š” ์ž‘์—… ๋‹จ์œ„๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ. -> ์ปค๋ฐ‹ ์ง์ „์—๋งŒ ๋™๊ธฐํ™”ํ•˜๋ฉด ๋จ

12. ์ค€์˜์† ์ƒํƒœ

  • ์˜์† ์ƒํƒœ์˜ Entity๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ถ„๋ฆฌ(detached)
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉ ๋ชปํ•จ

12.1. ์ค€์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

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

12.2. ์ค€์˜์† ์ƒํƒœ๋ฉด ์ง€์—ฐ ๋กœ๋”ฉ์„ ๋ชป์“ด๋‹ค.

์ง€์—ฐ ๋กœ๋”ฉ์„ ์“ฐ๋ ค๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์‚ด์•„์žˆ์–ด์•ผ ํ•œ๋‹ค.
์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ฃฝ์–ด์žˆ๋Š”๋ฐ ์ง€์—ฐ ๋กœ๋”ฉ์ด ์ ์šฉ๋œ ๊ฐ์ฒด๋ฅผ ํ„ฐ์น˜ํ•˜๋ฉด LazyInitializationException ์—๋Ÿฌ๊ฐ€ ํ„ฐ์ง„๋‹ค.(ํ˜„์—…์—์„œ ์ž์ฃผ ๋งŒ๋‚œ๋‹ค.)
์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ DB์ปค๋„ฅ์…˜ ๋“ฑ์„ ๋‹ค ๋“ค๊ณ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ ‡๋‹ค.

12.3. ํ”„๋ก์‹œ์™€ ์ฆ‰์‹œ๋กœ๋”ฉ(EAGER) ์ฃผ์˜

  • ๊ฐ€๊ธ‰์  ์ง€์—ฐ ๋กœ๋”ฉ(LAZY)์„ ์‚ฌ์šฉ
  • ์ฆ‰์‹œ ๋กœ๋”ฉ์„ ์ ์šฉํ•˜๋ฉด ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ SQL์ด ๋ฐœ์ƒ
  • ์ฆ‰์‹œ ๋กœ๋”ฉ์€ JPQL์—์„œ N+1 ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ๋‹ค.
  • @ManyToOne, @OneToOne์€ ๊ธฐ๋ณธ์ด ์ฆ‰์‹œ ๋กœ๋”ฉ -> LAZY๋กœ ์„ค์ •ํ•  ๊ฒƒ.
  • @OneToMany, @ManyToMany๋Š” ๊ธฐ๋ณธ์ด ์ง€์—ฐ ๋กœ๋”ฉ

13. JPQL

  • JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Entity ๊ฐ์ฒด ์ค‘์‹ฌ์œผ๋กœ ๊ฐœ๋ฐœ
  • ๋ฌธ์ œ๋Š” ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ
  • ๊ฒ€์ƒ‰์„ ํ•  ๋•Œ๋„ ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ Entity ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๊ฒ€์ƒ‰
  • ๋ชจ๋“  DB ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅ
  • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ DB์—์„œ ๋ถˆ๋Ÿฌ๋‚ด๋ ค๋ฉด, ๊ฒฐ๊ตญ ๊ฒ€์ƒ‰ ์กฐ๊ฑด์ด ํฌํ•จ๋œ SQL์ด ํ•„์š”

๊ทธ๋ž˜์„œ JPA๋Š” SQL์„ ์ถ”์ƒํ™”ํ•œ JPQL์ด๋ผ๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด ์ œ๊ณต.
SQL๊ณผ ๋ฌธ๋ฒ• ์œ ์‚ฌ(SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN).
JPQL์€ Entity ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ. (SQL์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ)

// ๊ฒ€์ƒ‰
String jpql = "select m From Member m where m.name like '%hello%'"; //Member๋Š” ๊ฐ์ฒด.

List<Member> result = em.createQuery(jpql, Member.class).getResultList();

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

13.1. JPQL ๋ฌธ๋ฒ•

  • Entity์™€ ์†์„ฑ์€ ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„
  • JPQL ํ‚ค์›Œ๋“œ๋Š” ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์•ˆํ•จ(SELECT, FROM, where)
  • Entity ์ด๋ฆ„์„ ์‚ฌ์šฉ, ํ…Œ์ด๋ธ” ์ด๋ฆ„์ด ์•„๋‹˜
  • ๋ณ„์นญ์€ ํ•„์ˆ˜
  • ๊ฒฐ๊ณผ ์กฐํšŒ API
    • query.getResultList() : ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ, ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜
    • query.getSingleResult() : ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํžˆ ํ•˜๋‚˜, ๋‹จ์ผ ๊ฐ์ฒด ๋ฐ˜ํ™˜(์ •ํ™•ํžˆ ํ•˜๋‚˜๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ)
  • ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ
  SELECT m FROM Member m where m.username=:username
  query.setParameter("username", usernameParam); //์ด๋ฆ„ ๊ธฐ์ค€ (๊ถŒ์žฅ)
  
  SELECT m FROM Member m where m.username=?1
  query.setParameter(1, usernameParam); //์œ„์น˜ ๊ธฐ์ค€
  • ํ”„๋กœ์ ์…˜
    • SELECT m FROM Member m -> ์—”ํ‹ฐํ‹ฐ ํ”„๋กœ์ ์…˜
    • SELECT m.team FROM Member m -> ์—”ํ‹ฐํ‹ฐ ํ”„๋กœ์ ์…˜
    • SELECT username, age FROM Member m -> ๋‹จ์ˆœ ๊ฐ’ ํ”„๋กœ์ ์…˜
    • new ๋ช…๋ น์–ด: ๋‹จ์ˆœ ๊ฐ’์„ DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒ
    • SELECT new jpabook.jpql.UserDTO(m.username,m.age) FROM Member m
    • DISTINCT๋Š” ์ค‘๋ณต ์ œ๊ฑฐ
  • ํŽ˜์ด์ง• API

    • JPA๋Š” ํŽ˜์ด์ง•์„ ๋‹ค์Œ ๋‘ API๋กœ ์ถ”์ƒํ™”
    • setFirstResult(int startPosition): ์กฐํšŒ ์‹œ์ž‘ ์œ„์น˜(0๋ถ€ํ„ฐ ์‹œ์ž‘)
    • setMaxResults(int maxResult): ์กฐํšŒํ•  ๋ฐ์ดํ„ฐ ์ˆ˜
    //ํŽ˜์ด์ง• ์ฟผ๋ฆฌ
    String jpql = "select m from Member m order by m.name desc";
    List<Member> resultList = em.createQuery(jpql, Member.class)
            .setFirstResult(10)
            .setMaxResults(20)
            .getResultList();
  • ์ง‘ํ•ฉ๊ณผ ์ •๋ ฌ

    select
    COUNT(m), //ํšŒ์›์ˆ˜
    SUM(m.age), //๋‚˜์ด ํ•ฉ
    AVG(m.age), //ํ‰๊ท  ๋‚˜์ด
    MAX(m.age), //์ตœ๋Œ€ ๋‚˜์ด
    MIN(m.age)  //์ตœ์†Œ ๋‚˜์ด
    from Member m
  • ์กฐ์ธ (์ผ๋ฐ˜ ์กฐ์ธ๊ณผ ๋ฌธ๋ฒ•์ด ์•ฝ๊ฐ„ ๋‹ค๋ฆ„)

    • ๋‚ด๋ถ€ ์กฐ์ธ
    • SELECT m FROM Member m [INNER] JOIN m.team t
    • ์™ธ๋ถ€ ์กฐ์ธ
    • SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
    • ์„ธํƒ€ ์กฐ์ธ (= ๋ง‰ ์กฐ์ธ. ์—ฐ๊ด€๊ด€๊ณ„ ์ƒ๊ด€ ์—†์ด ์กฐ์ธ)
    • select count(m) from Member m, Team t where m.username = t.name
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ 5.1๋ถ€ํ„ฐ ์„ธํƒ€ ์กฐ์ธ๋„ ์™ธ๋ถ€ ์กฐ์ธ ๊ฐ€๋Šฅ
    • fetch ์กฐ์ธ
    • Entity ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํ•œ๋ฒˆ์— ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•
    • ๋ณ„์นญ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
    • JPQL: select m from Member m join fetch m.team - Member๋ฅผ ์กฐํšŒํ•  ๋•Œ Team๊นŒ์ง€ ๊ฐ™์ด ๊ฐ€์ง€๊ณ  ์˜ค๋Š” ๊ฒƒ.
    • SQL: SELECT M.*, T.* FROM MEMBER T INNER JOIN TEAM T ON M.TEAM_ID=T.ID
    • fetch ์กฐ์ธ ์˜ˆ์‹œ
    String jpql = "select m from Member m join fetch m.team";
    
    List<Member> members = em.createQuery(jpql, Member.class).getResultList();
    
    for (Member member : members) {
        // fetch ์กฐ์ธ์œผ๋กœ Member์™€ Team์„ ํ•จ๊ป˜ ์กฐํšŒํ•ด์„œ ์ง€์—ฐ ๋กœ๋”ฉ ๋ฐœ์ƒ ์•ˆํ•จ
        System.out.println("username = " + member.getUsername() + ", " + "teamname = " + member.getTeam().name());
  • ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ

    • select function('group_concat', i.name) from Item i
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ์‚ฌ์šฉ ์ „ ๋ฐฉ์–ธ์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.
  • Named ์ฟผ๋ฆฌ

    • ๋ฏธ๋ฆฌ ์ •์˜ํ•ด์„œ ์ด๋ฆ„์„ ๋ถ€์—ฌํ•ด๋‘๊ณ  ์‚ฌ์šฉํ•˜๋Š” JPQL
    • ์–ด๋…ธํ…Œ์ด์…˜, XML์— ์ •์˜
    • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋”ฉ ์‹œ์ ์— ์ดˆ๊ธฐํ™” ํ›„ ์žฌ์‚ฌ์šฉ
    • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋”ฉ ์‹œ์ ์— ์ฟผ๋ฆฌ๋ฅผ ๊ฒ€์ฆ
  @Entity
  @NamedQuery(
          name = "Member.findByUsername",
          query = "select m from Member m where m.username = :username")
  public class Member {
      ...
  }
  List<Member> resultList =
    em.createNamedQuery("Member.findByUsername", Member.class)
          .setParameter("username", "ํšŒ์›1")
          .getResultList();

14. Spring Data JPA

14.1. Spring Data JPA ์ ์šฉ ์ „

public class MemberRepository {
  
  public void save(Member member) {...}
  public Member findOne(Long id) {...}
  public List<Member> findAll() {...} // ๊ณตํ†ต
  
  public Member findByName(String name) {...} // ๊ณตํ†ต ์•„๋‹˜
}

public class ItemRepository {

  public void save(Member member) {...}
  public Member findOne(Long id) {...}
  public List<Member> findAll() {...}
}

์œ„์™€ ๊ฐ™์ด ๊ฒฐ๊ตญ CRUD๊ฐ€ ๋ฐ˜๋ณต๋œ๋‹ค.
Spring Data JPA๋Š” ์ง€๋ฃจํ•˜๊ฒŒ ๋ฐ˜๋ณต๋˜๋Š” CRUD ๋ฌธ์ œ๋ฅผ ์„ธ๋ จ๋œ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐ.
๊ฐœ๋ฐœ์ž๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์ž‘์„ฑํ•˜๊ณ , ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•ด์„œ ์ฃผ์ž….

14.2. Spring Data JPA ์ ์šฉ ํ›„

Spring Data JPA๊ฐ€ ๋กœ๋”ฉ ์‹œ์ ์— ItemRepository๋ฅผ ํ™•์ธํ•˜๊ณ  ๊ตฌํ˜„ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.

public interface MemberRepository extends JpaRepository<Member, Long> {
    Member findByName(String name);
}

public interface ItemRepository extends JpaRepository<Item, Long> {
    // ๋น„์–ด์žˆ์Œ
}

๊ณตํ†ตํ™” ํ•  ์ˆ˜ ์—†์—ˆ๋˜ Member findByName(String name); ์„ ์ œ์™ธํ•˜๊ณ ๋Š” JpaRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฉด ๋์ด๋‹ค.
์ฆ‰, JpsRepository ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๊ณตํ†ต CRUD๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค.
์ œ๋„ค๋ฆญ์€ <์—”ํ‹ฐํ‹ฐ, ์‹๋ณ„์ž>๋กœ ์„ค์ •.

Spring Data JPA๋Š” Spring Data ํ”„๋กœ์ ํŠธ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์† ๋ฐ›์€ ๊ฒƒ์ด๋‹ค.
์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์˜ JpaRepository ์ธํ„ฐํŽ˜์ด์Šค ->
์Šคํ”„๋ง ๋ฐ์ดํ„ฐ์˜ PagingAndSortingRepository ์ธํ„ฐํŽ˜์ด์Šค ->
CrudRepository ์ธํ„ฐํŽ˜์ด์Šค ->
Repository ์ธํ„ฐํŽ˜์ด์Šค

14.3. ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ๊ธฐ๋Šฅ

  • ๋ฉ”์„œ๋“œ ์ด๋ฆ„๋งŒ์œผ๋กœ JPQL ์ฟผ๋ฆฌ ์ƒ์„ฑ

    public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByName(String name); //์ด๋ ‡๊ฒŒ๋งŒ ์ž‘์„ฑํ•˜๋ฉด ์•Œ์•„์„œ JPQL ์งœ์คŒ
    }
    • ์˜ˆ๋ฅผ ๋“ค์–ด List<Member> member = memberRepository.findByName("hello")์˜ ๊ฒฝ์šฐ ์‹คํ–‰๋  SQL์€
    SELECT*FROM MEMBER M WHERE M.NAME = 'hello'
    • ์ด๋ฆ„์œผ๋กœ ๊ฒ€์ƒ‰+์ •๋ ฌ ํ•˜๋Š” ๊ฒฝ์šฐ
    pulbic interface MemberRepository extends JpaRepository<Member, Long> {
        List<Member> findByName(String name, Sort sort); // Sort๊ฐ€ ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค.
    }
    SELECT * FROM MEMBER M WHERE M.NAME = 'hello' ORDER BY AGE DESC
    • ์ด๋ฆ„์œผ๋กœ ๊ฒ€์ƒ‰+์ •๋ ฌ+ํŽ˜์ด์ง• ํ•˜๋Š” ๊ฒฝ์šฐ
    public interface MemberRepository extends JpaRepository<Member, Long> {
        Page<Member> findByName(String name, Pageable pageable);
    }

    ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

    @RequestMapping("/search")
    Page<Member> search(@RequestParam("name") String name, Pageable pageable) {
      PageRequest pageRequest = PageRequest.of(1, 10); // ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ, ์‚ฌ์ด์ฆˆ
      return repository.findByName(name, pageRequest);
    }
  • @Query ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ฟผ๋ฆฌ ์ง์ ‘ ์ •์˜

    public interface MemberRepository extends JpaRepository<Member, Long> {
      
    @Query("select m from Member m where m.name = ?1")
    Member findByName(String name, Pageable pageable);
    }

14.4. ๋ฐ˜ํ™˜ ํƒ€์ž… ์ง€์ •

๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

List<Member> findByName(String name); // ์ปฌ๋ ‰์…˜
Member findByEmail(String email); // ๋‹จ๊ฑด

14.5. Web ํŽ˜์ด์ง•๊ณผ ์ •๋ ฌ ๊ธฐ๋Šฅ

์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ๊ฐ์ฒด๋ฅผ ๋ฐ”๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
/members?page=0&size=20&sort=name,desc

@RequestMapping(vlaue = "/members", method = RequestMethod.GET)
String list(Pageable pageable, Model model) {...}

14.6. Web ๋„๋ฉ”์ธ ํด๋ž˜์Šค ์ปจ๋ฒ„ํ„ฐ ๊ธฐ๋Šฅ

์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‹๋ณ„์ž๋กœ ๋„๋ฉ”์ธ ํด๋ž˜์Šค ์ฐพ์Œ
/members/100

@RequestMapping("/members/{memberId}")
Member member(@PathVariable("memberId") Member member) {
    return member;
}

15. QueryDSL

  • SQL๊ณผ JPQL์„ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋นŒ๋” API
  • JPA criteria์— ๋น„ํ•ด์„œ ํŽธ๋ฆฌํ•˜๊ณ  ์‹ค์šฉ์ ์ด๋‹ค.
  • ์˜คํ”ˆ์†Œ์Šค

15.1. SQL, JPQL์˜ ๋ฌธ์ œ์ 

  • SQL, JPQL์€ ๋ฌธ์ž, Type-check ๋ถˆ๊ฐ€๋Šฅ
  • ํ•ด๋‹น ๋กœ์ง ์‹คํ–‰ ์ „๊นŒ์ง€ ์ž‘๋™์—ฌ๋ถ€ ํ™•์ธ ๋ถˆ๊ฐ€(์ปดํŒŒ์ผ ์‹œ์ ์— ์•Œ ์ˆ˜ ์—†๋‹ค.)

15.2. QueryDSL ์žฅ์ 

  • ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ์ฝ”๋“œ๋กœ ์ž‘์„ฑ
  • ์ปดํŒŒ์ผ ์‹œ์ ์— ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ ๋ฐœ๊ฒฌ
  • ์ฝ”๋“œ ์ž๋™์™„์„ฑ(IDE ๋„์›€)
  • ๋‹จ์ˆœํ•˜๊ณ  ์‰ฌ์›€: ์ฝ”๋“œ ๋ชจ์–‘์ด JPQL๊ณผ ๊ฑฐ์˜ ๋น„์Šท
  • ๋™์  ์ฟผ๋ฆฌ

15.3. QueryDSL ์‚ฌ์šฉ

  • JPQL
    • select m from Member m where m.age > 18
  • QueryDSL
  JPAFactoryQuery query = new JPAQueryFactory(em);
  QMember m = QMember.member;
  
  List<Member> list = query.selectFrom(m)
                           .where(m.age.gt(18))
                           .orderBy(m.name.desc())
                           .fetch();

15.4. QueryDSL - ์กฐ์ธ

JPAQueryFactory query = new JPAQueryFactory(em);
QMember m = QMember.member;
QTeam t = QTeam.team;

List<Member> list = query.selectFrom(m)
                         .join(m.team, t)
                         .where(t.name.eq("teamA"))
                         .fetch();

15.5. QueryDSL - ํŽ˜์ด์ง• API

JPAQueryFactory query = new JPAQueryFactory(em);
QMember m = QMember.member;

List<Member> list = query.selectFrom(m)
                         .orderBy(m.age.desc())
                         .offset(10)
                         .limit(20)
                         .fetch();

15.6. QueryDSL - ๋™์  ์ฟผ๋ฆฌ

QueryDSL์„ ์“ฐ๋Š” ๊ฐ€์žฅ ํฐ ์ด์œ ๋Š” ๋™์  ์ฟผ๋ฆฌ ๋•Œ๋ฌธ

String name = "memebr";
int age = 9;

QMember m = QMember.member;

BooleanBuildere builder = new BooleanBuilder();
if (name != null) {
  builder.and(m.name.contains(name));
}
if (age != 0) {
  builder.and(m.age.gt(age);
}

List<Member> list = query.selectFrom(m)
                         .where(builder)
                         .fetch();

ํŠนํžˆ DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ QueryDSL์ด ํŠนํžˆ ์œ ์šฉํ•˜๋‹ค.

15.7. QueryDSL - ์ด๊ฒƒ์€ ์ž๋ฐ”๋‹ค!

return query.selectFrom(coupon)
            .where(
                coupon.type.eq(typeParam),
                coupon.status.eq("LIVE"), //์„œ๋น„์Šค ํ•„์ˆ˜ ์ œ์•ฝ์กฐ๊ฑด
                marketing.viewCount.lt(marketing.maxCount) //์„œ๋น„์Šค ํ•„์ˆ˜ ์ œ์•ฝ์กฐ๊ฑด
             )
             .fetch();

์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ œ์•ฝ์กฐ๊ฑด์„ ์กฐ๋ฆฝํ•  ์ˆ˜ ์žˆ๋‹ค. (๊ฐ€๋…์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ ์ œ๊ณ )

return query.selectFrom(coupon)
            .where(
                coupon.type.eq(typeParam),
                isServiceable()
            )
            .fetch();

private BooleanExpression isServiceable() {
    return coupon.status.eq("LIST") //์„œ๋น„์Šค ํ•„์ˆ˜ ์ œ์•ฝ์กฐ๊ฑด
           .and(marketing.viewCount.lt(marketing.maxCount)); //์„œ๋น„์Šค ํ•„์ˆ˜ ์ œ์•ฝ์กฐ๊ฑด
}