Spring DB ์ ‘๊ทผ ๊ธฐ์ˆ 


  • JDBC ๋Š” SQL ๋กœ ์„œ๋ฒ„๋ž‘ DB ์—ฐ๊ฒฐํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค.
  • JdbcTemplate ์„ ํ†ตํ•ด SQL ๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋‚ ๋ฆด ์ˆ˜ ์žˆ๋‹ค.
  • JPA ๊ธฐ์ˆ ์ด ๋“ฑ๋ก, ์ˆ˜์ •, ์‚ญ์ œ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋‚ ๋ ค์ค€๋‹ค.

์ˆœ์ˆ˜ JDBC

  • build.gradle ์—์„œ jdbc, h2 DB ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    runtimeOnly 'com.h2database:h2'
  • application.properties ์—์„œ ์Šคํ”„๋ง ๋ถ€ํŠธ DB ์—ฐ๊ฒฐ ์„ค์ •์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

    spring.datasource.url=jdbc:h2:tcp://localhost/~/test
    spring.datasource.driver-class-name=org.h2.Driver
    spring.datasource.username=sa

Jdbc Repository ๊ตฌํ˜„ ํ›„ ์Šคํ”„๋ง ์„ค์ • ๋ณ€๊ฒฝ

@Configuration
public class SpringConfig {
 
    private final DataSource dataSource;
 
    public SpringConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }
 
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
 
    @Bean
    public MemberRepository memberRepository() {
        // return new MemoryMemberRepository();
        return new JdbcMemberRepository(dataSource);
    }
}
  • ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ MemoryMemberRepository ๋ฅผ JdbcMemberRepository ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
  • DataSource ๋Š” DB Connection ์„ ํš๋“ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด๋‹ค.
  • ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” DB Connection ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ DataSource ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋งŒ๋“ค์–ด DI ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
  • MemberService ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ธ MemberRepository ๋ฅผ ์˜์กดํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— MemberRepository ์˜ ๊ตฌํ˜„์ฒด๋ฅผ ๋ชจ๋‘ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ณ€๊ฒฝ๋œ @Configuration ์„ ๋ฐ”ํƒ•์œผ๋กœ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ DI ๋ฅผ memberRepository ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.
  • OCP(Open-Closed Principle, ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™)
    • ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ , ์ˆ˜์ •, ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ๋‹ค.
    • ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๋‹คํ˜•์„ฑ์„ ํ†ตํ•ด OCP ์›์น™์„ ์ง€์ผœ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
  • ์Šคํ”„๋ง์˜ DI ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์ „ํ˜€ ์†๋Œ€์ง€ ์•Š๊ณ , ์„ค์ •๋งŒ์œผ๋กœ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

  • ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ๋‹จ์œ„์— @SpringBootTest ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์™€ ํ…Œ์ŠคํŠธ๋ฅผ ํ•จ๊ป˜ ์‹คํ–‰ํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ๋‹จ์œ„์— @Transactional ์„ ์ถ”๊ฐ€ํ•˜๋ฉด, ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ์ „์— Transaction ์„ ์‹œ์ž‘ํ•˜๊ณ , ํ…Œ์ŠคํŠธ ์™„๋ฃŒ ํ›„์— ํ•ญ์ƒ ๋กค๋ฐฑํ•œ๋‹ค.

์Šคํ”„๋ง JdbcTemplate

  • ์ˆœ์ˆ˜ JDBC ์—์„œ ์ค‘๋ณต๋˜๋Š” ์ฝ”๋“œ๋“ค์„ ์ œ๊ฑฐํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ DB ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š”๋ฐ ๋„์›€์„ ์ฃผ๋Š” template ์ด๋‹ค.
public JdbcTemplateMemberRepository(DataSource dataSource) {
    jdbcTemplate = new JdbcTemplate(dataSource);
}
  • ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ DI ๋ฅผ ํ†ตํ•ด JdbcTemplate ์— DataSource ๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
 
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", member.getName());
 
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
member.setId(key.longValue());
  • SimpleJdbcInsert ๋ฅผ ํ†ตํ•ด INSERT ์ฟผ๋ฆฌ๋ฌธ๊ณผ ํ•จ๊ป˜ ์ž๋™์ƒ์„ฑ๋˜๋Š” id ๊ฐ’์„ ๋ฐ˜ํ™˜ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

JPA


JPA ๋ฅผ ํ†ตํ•ด ๊ธฐ๋ณธ์ ์ธ SQL ์„ JPA ๊ฐ€ ๋งŒ๋“ค์–ด ์‹คํ–‰ํ•˜๊ณ , SQL ์ค‘์‹ฌ ์„ค๊ณ„์—์„œ ๊ฐ์ฒด ์ค‘์‹ฌ ์„ค๊ณ„๋กœ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, JPA ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ํฌ๊ฒŒ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค. JPA ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋ฉฐ ๊ตฌํ˜„์ฒด๋กœ Hibernate ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. JPA ๋Š” ORM ๊ธฐ์ˆ ๋กœ์„œ Object Relational Mapping ์˜ ์•ฝ์ž์ด๋‹ค. ์ฆ‰, ๊ฐ์ฒด์™€ RDB ๋ฅผ ๋งคํ•‘ํ•ด์ฃผ๋Š” ๊ธฐ์ˆ ์ด๋‹ค.

build.gradle ์— dependencies ์ถ”๊ฐ€

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
 
    // implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
 
    runtimeOnly 'com.h2database:h2'
 
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

spring-boot-starter-data-jpa ๋‚ด๋ถ€์— jdbc ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํฌํ•จํ•˜๊ธฐ ๋•Œ๋ฌธ์— jdbc ๋Š” ์ œ๊ฑฐํ•ด๋„ ๋œ๋‹ค.

application.properties ์— ์„ค์ • ์ถ”๊ฐ€

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
 
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
  • show-sql ์„ค์ •์„ ํ†ตํ•ด JPA ๊ฐ€ ์ƒ์„ฑํ•˜๋Š” SQL ์„ ์ถœ๋ ฅํ•œ๋‹ค.
  • ddl-auto JPA ๋Š” ํ…Œ์ด๋ธ”์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, none ์„ ์„ค์ •ํ•ด์ฃผ๋ฉด ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฒŒ๋œ๋‹ค.
    • create ์„ค์ • ์‹œ ์—”ํ‹ฐํ‹ฐ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ…Œ์ด๋ธ”๋„ ์ง์ ‘ ์ƒ์„ฑํ•ด์ค€๋‹ค.

JPA ์—”ํ‹ฐํ‹ฐ ๋งคํ•‘

package hello.hellospring.domain;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
 
@Entity
public class Member {
 
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

@Entity ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋ผ๋Š” ๊ฒƒ์„ ๋ช…์‹œํ•œ๋‹ค. @Id ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ํ•„๋“œ๊ฐ€ DB ์˜ Id ๊ฐ’์ด๋ผ๊ณ  ๋ช…์‹œํ•ด์ค€๋‹ค. @GeneratedValue(strategy = GenerationType.IDENTITY) ๋ฅผ ํ†ตํ•ด AUTO_INCREMENT ๋˜๋Š” ๊ฐ’์ด๋ผ๋Š” ๊ฒƒ์„ ๋ช…์‹œํ•ด์ค€๋‹ค.

JPA ํšŒ์› Repository

package hello.hellospring.repository;
 
import hello.hellospring.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
 
public class JpaMemberRepository implements MemberRepository {
 
    private final EntityManager em;
 
    public JpaMemberRepository(EntityManager em) {
         this.em = em;
    }
 
    public Member save(Member member) {
        em.persist(member);
        return member;
    }
 
    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id);
        return Optional.ofNullable(member);
    }
 
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
 
    public Optional<Member> findByName(String name) {
        List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
                .setParameter("name", name)
                .getResultList();
 
        return result.stream().findAny();
    }
}

Spring DI ๋ฅผ ํ†ตํ•ด EntityManager ๋ฅผ ์ฃผ์ž…ํ•ด์ค€๋‹ค. ์ฃผ์ž…๋œ em ์„ ํ†ตํ•ด ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. JPA ๋Š” ์กฐํšŒ ๋กœ์ง์ด Id ๊ฐ’์œผ๋กœ ์ฐพ๋Š” ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์—, findByName(String name) ์™€ ๊ฐ™์€ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„  JPQL ์ด๋ผ๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด JPA ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Spring Data ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด JPQL ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.

Service ๊ณ„์ธต์— Transaction ์ถ”๊ฐ€

import org.springframework.transaction.annotation.Transactional
 
@Transactional
public class MemberService {}

๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด Transaction ์•ˆ์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— @Transactional ์• ๋„ˆํ…Œ์ด์…˜์ด ํ•„์š”ํ•˜๋‹ค.

JPA ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์Šคํ”„๋ง ์„ค์ • ๋ณ€๊ฒฝ

package hello.hellospring;
 
import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
 
@Configuration
public class SpringConfig {
 
    private final DataSource dataSource;
    private final EntityManager em;
 
    public SpringConfig(DataSource dataSource, EntityManager em) {
        this.dataSource = dataSource;
        this.em = em;
    }
 
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
 
    @Bean
    public MemberRepository memberRepository() {
        // return new MemoryMemberRepository();
        // return new JdbcMemberRepository(dataSource);
        // return new JdbcTemplateMemberRepository(dataSource);
        return new JpaMemberRepository(em);
    }
}

EntityManager ๋ฅผ DI ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ SpringConfig ํด๋ž˜์Šค์—์„œ ์ถ”๊ฐ€์ ์ธ ์„ค์ •์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

Spring Data JPA


Spring Boot ์™€ JPA ๋งŒ ์‚ฌ์šฉํ•ด๋„ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ๋งŽ์ด ์ฆ๊ฐ€ํ•˜๋ฉฐ, ๊ฐœ๋ฐœํ•ด์•ผ ํ•  ์ฝ”๋“œ๋„ ํ™•์—ฐํžˆ ์ค„์–ด๋“ ๋‹ค. Spring Data JPA ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, Repository ๊ตฌํ˜„ ํด๋ž˜์Šค ์—†์ด ์ธํ„ฐํŽ˜์ด์Šค ๋งŒ์œผ๋กœ ๊ฐœ๋ฐœ์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ๋‹ค. Spring Data JPA ๋Š” JPA ๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๋„๊ตฌ์ผ ๋ฟ์ด๊ธฐ ๋•Œ๋ฌธ์— JPA ๋ฅผ ๋จผ์ € ํ•™์Šตํ•ด์•ผํ•œ๋‹ค.

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ํšŒ์› Repository

package hello.hellospring.repository;
 
import hello.hellospring.domain.Member;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
 
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
 
    @Override
    Optional<Member> findByName(String name);
}

JpaRepository ์™€ ์‚ฌ์šฉํ•  Repository ์ธ MemberRepository ๋ฅผ ์ƒ์†๋ฐ›์€ ์ƒˆ๋กœ์šด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํ›„ ๊ธฐ์กด์— JPQL ๋กœ ๊ตฌํ˜„ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ Override ํ•ด์ฃผ๋ฉด ๋์ด๋‹ค. Spring Data ๊ฐ€ ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฐพ์•„ ๋ฉ”์„œ๋“œ๋ฅผ ์•Œ์•„์„œ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ํšŒ์› Repository ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ • ๋ณ€๊ฒฝ

package hello.hellospring;
 
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class SpringConfig {
 
    private final MemberRepository memberRepository;
 
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
 
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository);
    }
}

ํ•ด๋‹น ์„ค์ •์„ ํ†ตํ•ด Spring ์ด ์•Œ์•„์„œ SpringDataJpaMemberRepository ๋ฅผ ์ฐพ์•„ DI ํ•ด์ค€๋‹ค. ์ฃผ์˜ํ•  ์ ์€, SpringDataJpaMemberRepository ์™ธ์— ๋‹ค๋ฅธ ๊ตฌํ˜„์ฒด์— @Repository ์• ๋„ˆํ…Œ์ด์…˜์ด ์ถ”๊ฐ€๋˜์–ด ์žˆ์œผ๋ฉด, Bean ์ด 2๊ฐ€์ง€๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ์ œ๊ณต ๊ธฐ๋Šฅ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๊ธฐ๋ณธ์ ์ธ CRUD ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋˜ํ•œ, findByName() findByEmail() ์ฒ˜๋Ÿผ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ๋งŒ์œผ๋กœ๋„ JPQL ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ์กฐํšŒ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ, PagingAndSortingRepository ๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง• ๊ธฐ๋Šฅ๋„ ์ž๋™์œผ๋กœ ์ œ๊ณตํ•œ๋‹ค. ์‹ค๋ฌด์—์„œ๋Š” JPA ์™€ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๋ณต์žกํ•œ ๋™์  ์ฟผ๋ฆฌ๋Š” Querydsl ์ด๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. Querydsl ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์ฟผ๋ฆฌ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ๋™์  ์ฟผ๋ฆฌ๋„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์กฐํ•ฉ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์šด ์ฟผ๋ฆฌ๋Š” JPA ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, JdbcTemplate ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

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


JPA ์™€ ๋ชจ๋˜ ์ž๋ฐ” ๋ฐ์ดํ„ฐ ์ €์žฅ ๊ธฐ์ˆ 

ํ˜„๋Œ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๊ฐ์ฒด ์ง€ํ–ฅ ์–ธ์–ด๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๊ด€๊ณ„ํ˜• DB ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ฆ‰, ์ง€๊ธˆ ์‹œ๋Œ€๋Š” ๊ฐ์ฒด๋ฅผ ๊ด€๊ณ„ํ˜• DB ์— ์ €์žฅํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

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

๋ฌดํ•œ ๋ฐ˜๋ณต๋˜๋Š” ์ง€๋ฃจํ•œ CRUD ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋ฉฐ, ๊ฐ์ฒด ์†์„ฑ์ด ์ถ”๊ฐ€๋  ๋•Œ ๋งˆ๋‹ค ์•„๋ž˜์™€ ๊ฐ™์ด SQL ์ฟผ๋ฆฌ๋ฌธ์ด ์ž์ฃผ ๋ณ€๊ฒฝ๋œ๋‹ค.

public class Member {
    private String memberId;
    private String name;
    private String tel;
}
INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
UPDATE MEMBER SET ... TEL = ?

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

๊ทธ๋Ÿผ ์™œ ๊ตณ์ด SQL?

๊ฐ์ฒด๋ฅผ ์˜๊ตฌ ๋ณด๊ด€ํ•˜๋Š” ๋‹ค์–‘ํ•œ ์ €์žฅ์†Œ๊ฐ€ ์กด์žฌํ•˜์ง€๋งŒ, ๊ทธ ์ค‘์— ๊ฐ€์žฅ ํ˜„์‹ค์ ์ธ ๋Œ€์•ˆ์ด ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜€์„ ๋ฟ์ด๋‹ค. ์ฆ‰, SQL ์— ์˜์กด์ ์ธ ๊ฐœ๋ฐœ์„ ํ”ผํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• DB ์˜ ์ฐจ์ด

1. ์ƒ์†
2. ์—ฐ๊ด€๊ด€๊ณ„
3. ๋ฐ์ดํ„ฐ ํƒ€์ž…
4. ๋ฐ์ดํ„ฐ ์‹๋ณ„ ๋ฐฉ๋ฒ•

๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• DB ์—๋Š” ์œ„์™€ ๊ฐ™์€ ์ฐจ์ด์ ๋“ค์ด ์žˆ๋Š”๋ฐ, ๊ฐ„๋‹จํžˆ ์ƒ์†๊ณผ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์‚ดํŽด๋ณด์ž. ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—๋Š” ์ƒ์† ๊ด€๊ณ„๊ฐ€ ์กด์žฌํ•˜์ง€๋งŒ, ๊ด€๊ณ„ํ˜• DB ์—๋Š” ์—†๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์—ฐ๊ด€๊ด€๊ณ„ ๋˜ํ•œ, ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” getter ๋ฅผ ํ†ตํ•ด ์—ฐ๊ด€๋œ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ค์ง€๋งŒ, ๊ด€๊ณ„ํ˜• DB ์—์„œ๋Š” PK, FK ๋“ฑ์œผ๋กœ ์—ฐ๊ด€๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

์ƒ์†

๊ฐ์ฒด์˜ ์ƒ์† ๊ด€๊ณ„ ์„ค๊ณ„๋ฅผ ์œ„์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด๋ƒˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ์œ„์™€ ๊ฐ™์ด ์„ค๊ณ„๋œ ๊ฐ์ฒด๋ฅผ DB ์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด์„  ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ? ๊ฐ์ฒด์˜ ์ƒ์† ๊ด€๊ณ„์™€ ๊ทธ๋‚˜๋งˆ ์œ ์‚ฌํ•œ ์Šˆํผํƒ€์ž…, ์„œ๋ธŒํƒ€์ž… ๋ชจ๋ธ๋กœ DB ์— ์ €์žฅํ•œ๋‹ค. ํ…Œ์ด๋ธ”์„ ์„ค๊ณ„ํ•˜๊ณ  ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•˜๊ณ  ์กฐํšŒํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณด์ž.

Album ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•˜๋ ค๋ฉด?

1. ๊ฐ์ฒด ๋ถ„ํ•ด
2. INSERT INTO ITEM โ€ฆ
3. INSERT INTO ALBUM โ€ฆ

์—ฌ๊ธฐ๊นŒ์ง€๋Š” ๊ดœ์ฐฎ์€ ๊ฒƒ ๊ฐ™๋‹ค.

Album ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๋ ค๋ฉด?

1. ๊ฐ๊ฐ์˜ ํ…Œ์ด๋ธ”์— ๋”ฐ๋ฅธ JOIN SQL ์ž‘์„ฑโ€ฆ
2. ๊ฐ๊ฐ์˜ ๊ฐ์ฒด ์ƒ์„ฑโ€ฆ

์ƒ์ƒ๋งŒ ํ•ด๋„ ๋ณต์žกํ•˜๋‹ค. ๊ทธ๋ž˜์„œ DB ์— ์ €์žฅํ•  ๊ฐ์ฒด์—๋Š” ์ƒ์† ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

DB ๊ฐ€ ์•„๋‹ˆ๋ผ ์ž๋ฐ” ์ปฌ๋ ‰์…˜์— ์ €์žฅํ•œ๋‹ค๋ฉด?

list.add(album);
Album album = list.get(albumId);
// ๋ถ€๋ชจ ํƒ€์ž…์œผ๋กœ ์กฐํšŒ ํ›„ ๋‹คํ˜•์„ฑ ํ™œ์šฉ๋„ ๊ฐ€๋Šฅ
Item item = list.get(albumId);

๋ฌธ๋“ ์ž๋ฐ” ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ํ›จ์”ฌ ๊ฐ„ํŽธํ•  ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ์‚ด์‚ด ๋“ ๋‹ค.

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

์ด๋ฒˆ์—” OOP ์™€ RDB ์˜ ์—ฐ๊ด€๊ด€๊ณ„์— ์–ด๋–ค ์ฐจ์ด์ ์ด ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž.

member.getTeam();

๊ฐ์ฒด๋Š” ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ฐ์ฒด๋Š” ๋‹จ๋ฐฉํ–ฅ ์ฐธ์กฐ๊ธฐ ๋•Œ๋ฌธ์— Team ์œผ๋กœ Member ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค.

JOIN ON M.TEAM_ID = T.TEAM_ID

ํ…Œ์ด๋ธ”์€ ์™ธ๋ž˜ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋•Œ๋ฌธ์— Team ์œผ๋กœ Member ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ํ…Œ์ด๋ธ”์€ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

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

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

class Member {
    String id;
    Long teamId;
    String username;
}
class Team {
    Long id;
    String name;
}
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...

ํ•˜์ง€๋งŒ, Member ๋‚ด๋ถ€์— teamId ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์ด ๋ญ”๊ฐ€ ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ด์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™๋‹ค. ๊ฐ์ฒด ์ง€ํ–ฅ์ ์œผ๋กœ ๋ชจ๋ธ๋ง์„ ๋ณ€๊ฒฝํ•ด๋ณด์ž.

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

class Member {
    String id;
    Team team;
    String username;
    Team getTeam() {
        return team;
    }
}
class Team {
    Long id;
    String name;
}
// member.getTeam().getId() ๋กœ TEAM_ID ๋ฅผ ๊ฐ€์ ธ์˜ด
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...

๋ชจ๋ธ๋ง์„ ๊ฐ์ฒด์ง€ํ–ฅ์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์ž, SQL ๋ฌธ์—์„œ TEAM_ID ๋ฅผ ์š”๊ตฌํ•  ๋•Œ, member.getTeam().getId() ์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•ด์ง„๋‹ค. ์—ฌ๊ธฐ๊นŒ์ง„ ๊ดœ์ฐฎ์€ ๊ฒƒ ๊ฐ™๋‹ค.

๊ฐ์ฒด ๋ชจ๋ธ๋ง ์กฐํšŒ

ํ•˜์ง€๋งŒ ์กฐํšŒ๋ฅผ ํ•  ๊ฒฝ์šฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

SELECT M.*, T.*
  FROM MEMBER M
  JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
public Member find(String memberId) {
    //SQL ์‹คํ–‰ ...
    Member member = new Member();
    //๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ํšŒ์› ๊ด€๋ จ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ์ž…๋ ฅ
    Team team = new Team();
    //๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ํŒ€ ๊ด€๋ จ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ์ž…๋ ฅ
    //ํšŒ์›๊ณผ ํŒ€ ๊ด€๊ณ„ ์„ค์ •
    member.setTeam(team); //
    return member;
}

JOIN ๊ตฌ๋ฌธ์ด ํฌํ•จ๋œ SQL ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๊ฐ๊ฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค. ์ž๋ฐ” ์ปฌ๋ ‰์…˜ API ๊ฐ€ ๊ทธ๋ฆฌ์›Œ์ง€๋Š” ์ˆœ๊ฐ„์ด๋‹ค.

๊ฐ์ฒด ๋ชจ๋ธ๋ง, ์ž๋ฐ” ์ปฌ๋ ‰์…˜์— ๊ด€๋ฆฌ

list.add(member);
Member member = list.get(memberId);
Team team = member.getTeam();

์œ„ ์ฒ˜๋Ÿผ ์ž๋ฐ” ์ปฌ๋ ‰์…˜ API ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ •๋ง ์‰ฌ์šธํ…๋ฐโ€ฆ

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

๊ฐ์ฒด๋Š” ์ž์œ ๋กญ๊ฒŒ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ฒ˜์Œ ์‹คํ–‰ํ•˜๋Š” SQL ์— ๋”ฐ๋ผ ํƒ์ƒ‰ ๋ฒ”์œ„๊ฐ€ ๊ฒฐ์ •๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜์™€ ๊ฐ™์€ SQL ์„ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

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

๊ฐ์ฒด ์ƒ์„ฑ ์‹œ์ ์— team ํ•„๋“œ๋งŒ ์ฑ„์›Œ ๋„ฃ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— member.getOrder() ํ˜ธ์ถœ ์‹œ null ๊ฐ’์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

์—”ํ‹ฐํ‹ฐ ์‹ ๋ขฐ ๋ฌธ์ œ

์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์€ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ ์‹ ๋ขฐ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

class MemberService {
    ...
    public void process() {
        Member member = memberDAO.find(memberId);
        member.getTeam(); //???
        member.getOrder().getDelivery(); // ???
    }
}

๋‹ค๋ฅธ ํŒ€์› ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ฐœ๋ฐœํ•œ memberDAO.find() ๋ฅผ ํ†ตํ•ด member ๋ฅผ ์กฐํšŒํ•  ๊ฒฝ์šฐ, memberDAO ๋‚ด๋ถ€์—์„œ ์–ด๋–ค ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜์—ˆ๊ณ , ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ๋ฆฝํ•˜์˜€๋Š”์ง€ ํ™•์ธํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ๋ฐ˜ํ™˜๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋‹ค.

๋ชจ๋“  ๊ฐ์ฒด๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋”ฉํ•  ์ˆ˜๋Š” ์—†์„๊นŒ?

//Member๋งŒ ์กฐํšŒ
memberDAO.getMember();
//Member์™€ Team ์กฐํšŒ
memberDAO.getMemberWithTeam();
//Member,Order,Delivery ์กฐํšŒ
memberDAO.getMemberWithOrderWithDelivery();

์ƒํ™ฉ์— ๋”ฐ๋ผ ๋™์ผํ•œ ํšŒ์› ์กฐํšŒ ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ๋ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

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

Layered Architecture ์—์„œ๋Š” ๊ทธ ๋‹ค์Œ ๊ณ„์ธต์—์„œ ์‹ ๋ขฐํ•˜๊ณ  ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋ฌผ๋ฆฌ์ ์œผ๋กœ๋Š” ๊ณ„์ธต์ด service ์™€ dao ๋กœ ๋‚˜๋‰˜์–ด์ ธ ์žˆ์ง€๋งŒ, ๋…ผ๋ฆฌ์ ์œผ๋ก  ์–ด๋Š์ •๋„ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค. ์ฆ‰, dependency ๊ฐ€ ์ข‹์ง€ ์•Š๋‹ค.

DB ์กฐํšŒ์™€ ์ž๋ฐ” ์ปฌ๋ ‰์…˜ ์กฐํšŒ ๋น„๊ตํ•˜๊ธฐ

String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; //๋‹ค๋ฅด๋‹ค.
class MemberDAO {
    public Member getMember(String memberId) {
        String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
        ...
        //JDBC API, SQL ์‹คํ–‰
        return new Member(...);
    }
}
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; //๊ฐ™๋‹ค.

๊ฐ์ฒด๋‹ต๊ฒŒ ๋ชจ๋ธ๋ง ํ• ์ˆ˜๋ก ๋งคํ•‘ ์ž‘์—…๋งŒ ๋Š˜์–ด๋‚œ๋‹คโ€ฆ

SQL ์— ๋งž์ถฐ์„œ ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ ์ „์†ก์šฉ์œผ๋กœ๋งŒ ์„ค๊ณ„๋ฅผ ํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค. ๊ฐ์ฒด ์ง€ํ–ฅ์ ์œผ๋กœ ์„ค๊ณ„๋ฅผ ํ•  ์ˆœ ์žˆ์–ด๋„, ROI ๊ฐ€ ์•ˆ ๋‚˜์˜จ๋‹ค.

๊ฐ์ฒด๋ฅผ ์ž๋ฐ” ์ปฌ๋ ‰์…˜์— ์ €์žฅ ํ•˜๋“ฏ์ด DB ์— ์ €์žฅํ•  ์ˆœ ์—†์„๊นŒ?

๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด ๋ฐ”๋กœ JPA(Java Persistence API) ์ด๋‹ค.

JPA ์†Œ๊ฐœ


JPA ๋ž€?

Java Persistence API ์˜ ์•ฝ์ž์ด๋ฉฐ, Java ์ง„์˜์˜ ORM ๊ธฐ์ˆ  ํ‘œ์ค€์ด๋‹ค.

ORM ์ด ๋ญ์•ผ?

Object-Relational Mapping ์˜ ์•ฝ์ž๋กœ, ๊ฐ์ฒด๋Š” ๊ฐ์ฒด๋Œ€๋กœ ์„ค๊ณ„ํ•˜๊ณ , ๊ด€๊ณ„ํ˜• DB ๋Š” ๊ด€๊ณ„ํ˜• DB ๋Œ€๋กœ ์„ค๊ณ„ํ•˜์—ฌ ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• DB ์ค‘๊ฐ„์—์„œ ์„œ๋กœ๋ฅผ ๋งคํ•‘ํ•ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค. ๋Œ€์ค‘์ ์ธ ์–ธ์–ด์—๋Š” ๋Œ€๋ถ€๋ถ„ ORM ๊ธฐ์ˆ ์ด ์กด์žฌํ•œ๋‹ค.

JPA ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ JDBC ์‚ฌ์ด์—์„œ ๋™์ž‘

๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹Œ JPA ๊ฐ€ JDBC ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ SQL ์„ ์ƒ์„ฑํ•˜๊ณ  DB ์™€ ์†Œํ†ตํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

JPA ๋™์ž‘ - ์ €์žฅ

JPA ๋™์ž‘ - ์กฐํšŒ

JAP ๋Š” ํ‘œ์ค€ ๋ช…์„ธ

JPA ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ์Œ์ด๋‹ค. JPA 2.1 ํ‘œ์ค€ ๋ช…์„ธ๋ฅผ ๊ตฌํ˜„ํ•œ ์—ฌ๋Ÿฌ ๊ตฌํ˜„์ฒด๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, ๊ทธ์ค‘ ์ œ์ผ ์œ ๋ช…ํ•œ 3๊ฐ€์ง€ ๊ตฌํ˜„์ฒด๊ฐ€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ, EclipseLink, DataNucleus ์ด๋‹ค. ์ด ์ค‘ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ธ๋‹ค.

JPA ๋ฅผ ์™œ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

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

์ƒ์‚ฐ์„ฑ - JPA ์™€ CRUD

  • ์ €์žฅ - jpa.persist(member)
  • ์กฐํšŒ - Member member = jpa.find(memberId)
  • ์ˆ˜์ • - member.setName(โ€๋ณ€๊ฒฝํ•  ์ด๋ฆ„")
  • ์‚ญ์ œ - jpa.remove(member) ์œ„์ฒ˜๋Ÿผ SQL ๊ตฌ๋ฌธ ์—†์ด ์ž๋ฐ” ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ ์ง€๋ณด์ˆ˜ - JPA ํ•„๋“œ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋จ, SQL ์€ JPA ๊ฐ€ ์ฒ˜๋ฆฌ

๊ธฐ์กด์— ๊ฐ์ฒด ์†์„ฑ์ด ์ถ”๊ฐ€๋  ๋•Œ ์ฟผ๋ฆฌ๋ฌธ์„ ๋ชจ๋‘ ์ˆ˜์ •ํ•ด์ฃผ์–ด์•ผํ•˜์ง€๋งŒ, JPA ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ๊ฐ์ฒด ์†์„ฑ๊ณผ DB ์ปฌ๋Ÿผ๋งŒ ์ถ”๊ฐ€ํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.

JPA ์™€ ์ƒ์† - ์ €์žฅ

JPA ์‚ฌ์šฉ ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ณผ์ •์—์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•  ์ผ์ด ์•„๋ž˜์™€ ๊ฐ™์•„์ง€๊ณ ,

jpa.persist(album);

๋‚˜๋จธ์ง€ SQL ์ฟผ๋ฆฌ ์ƒ์„ฑ๊ณผ ๊ฐ™์€ ์ผ๋“ค์€ JPA ๊ฐ€ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค.

INSERT INTO ITEM ...
INSERT INTO ALBUM ...

JPA ์™€ ์ƒ์† - ์กฐํšŒ

์กฐํšŒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•  ์ผ์ด ์•„๋ž˜์™€ ๊ฐ™์•„์ง€๊ณ ,

Album album = jpa.find(Album.class, albumId);

๋‚˜๋จธ์ง„ JPA ๊ฐ€ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค.

SELECT I.*, A.*
 FROM ITEM I
 JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID

JPA ์™€ ์—ฐ๊ด€๊ด€๊ณ„, ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰

์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ์—ฐ๊ด€๊ด€๊ณ„ ์ €์žฅ ์‹œ,

member.setTeam(team);
jpa.persist(member);

์ดํ›„ ์กฐํšŒํ•˜์˜€์„ ๋•Œ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰์ด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();

์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ, ๊ณ„์ธต

class MemberService {
    ...
    public void process() {
        Member member = memberDAO.find(memberId);
        member.getTeam(); //์ž์œ ๋กœ์šด ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰
        member.getOrder().getDelivery();
    }
}

์œ„์ฒ˜๋Ÿผ SQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜์—ฌ ์กฐํšŒํ•  ๋•Œ ๋ณด๋‹ค ์ž์œ ๋กญ๊ฒŒ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๊ธฐ์—, ์‹ ํšŒํ•  ์ˆ˜ ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

JPA ์™€ ๋น„๊ตํ•˜๊ธฐ

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ๊ฐ™์Œ์„ ๋ณด์žฅํ•œ๋‹ค.

String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //๊ฐ™๋‹ค

JPA์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™” - 1์ฐจ ์บ์‹œ์™€ ๋™์ผ์„ฑ(identity) ๋ณด์žฅ

๊ฐ™์€ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ๋Š” ๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์•ฝ๊ฐ„์˜ ์กฐํšŒ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒํ•˜๊ณ , DB Isolation Level ์ด Read Commit ์ด์–ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” Repeatable Read ๋ฅผ ๋ณด์žฅํ•œ๋‹ค.

String memberId = "100";
Member member1 = jpa.find(Member.class, memberId); // SQL
Member member2 = jpa.find(Member.class, memberId); // ์บ์‹œ
member1 == member2; //๊ฐ™๋‹ค

์œ„์ฒ˜๋Ÿผ ์บ์‹œ๋ฅผ ํ†ตํ•ด SQL ์„ 1๋ฒˆ๋งŒ ์‹คํ–‰ํ•ด๋„ ๋œ๋‹ค.

JPA์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™” - ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” INSERT ์ง€์—ฐ

transaction.begin(); // [ํŠธ๋žœ์žญ์…˜] ์‹œ์ž‘
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//์—ฌ๊ธฐ๊นŒ์ง€ INSERT SQL์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.
//์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— INSERT SQL์„ ๋ชจ์•„์„œ ๋ณด๋‚ธ๋‹ค.
transaction.commit(); // [ํŠธ๋žœ์žญ์…˜] ์ปค๋ฐ‹

์œ„์ฒ˜๋Ÿผ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๋•Œ๊นŒ์ง€ INSERT SQL ์„ ๋ชจ์•„์„œ JDBC BATCH SQL ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด ํ•œ๋ฒˆ์— SQL ์„ ์ „์†กํ•œ๋‹ค.

JPA์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™” - ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” UPDATE ์ง€์—ฐ

transaction.begin(); // [ํŠธ๋žœ์žญ์…˜] ์‹œ์ž‘
changeMember(memberA);
deleteMember(memberB);
๋น„์ฆˆ๋‹ˆ์Šค_๋กœ์ง_์ˆ˜ํ–‰(); //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ˆ˜ํ–‰ ๋™์•ˆ DB ๋กœ์šฐ ๋ฝ์ด ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
//์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— UPDATE, DELETE SQL์„ ๋ณด๋‚ธ๋‹ค.
transaction.commit(); // [ํŠธ๋žœ์žญ์…˜] ์ปค๋ฐ‹

์œ„์ฒ˜๋Ÿผ UPDATE, DELETE๋กœ ์ธํ•œ ๋กœ์šฐ(ROW)๋ฝ ์‹œ๊ฐ„์„ ์ตœ์†Œํ™”ํ•˜๊ณ , ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‹œ UPDATE, DELETE SQL ์‹คํ–‰ํ•˜๊ณ , ๋ฐ”๋กœ ์ปค๋ฐ‹ํ•œ๋‹ค.

JPA์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™” - ์ง€์—ฐ ๋กœ๋”ฉ๊ณผ ์ฆ‰์‹œ ๋กœ๋”ฉ

Member member = memberDAO.find(memberId);
// SELECT * FROM MEMBER ์‹คํ–‰
Team team = member.getTeam();
String teamName = team.getName();
// SELECT * FROM TEAM ์‹คํ–‰

์ง€์—ฐ ๋กœ๋”ฉ์€ ๊ฐ์ฒด๊ฐ€ ์‹ค์ œ ์‚ฌ์šฉ๋  ๋•Œ ๋กœ๋”ฉ๋˜์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ตœ์ ํ™” ๋œ๋‹ค.

Member member = memberDAO.find(memberId);
// SELECT M.*, T.* FROM MEMBER JOIN TEAM โ€ฆ ์‹คํ–‰
Team team = member.getTeam();
String teamName = team.getName();

์ฆ‰์‹œ ๋กœ๋”ฉ์€ JOIN SQL ๋กœ ํ•œ๋ฒˆ์— ์—ฐ๊ด€๋œ ๊ฐ์ฒด๊นŒ์ง€ ๋ฏธ๋ฆฌ ์กฐํšŒํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ตœ์ ํ™” ๋œ๋‹ค.

ORM ์€ ๊ฐ์ฒด์™€ RDB ๋‘ ๊ธฐ๋‘ฅ ์œ„์— ์žˆ๋Š” ๊ธฐ์ˆ 

ORM ์„ ์ž˜ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  OOP ์™€ RDB ์— ๋Œ€ํ•œ ์ง€์‹ ๋ชจ๋‘ ์ค‘์š”ํ•˜๋‹ค. OOP ์–ธ์–ด๋Š” RDB ์— ๋น„ํ•ด ๋ณ€ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๊ธฐ ๋•Œ๋ฌธ์— RDB ์— ๋Œ€ํ•œ ๊ณต๋ถ€๋ฅผ ๊พธ์ค€ํžˆ ํ•˜์ž!

Hello JPA - ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ


pom.xml ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jpa-basic</groupId>
    <artifactId>ex1-hello-jpa</artifactId>
    <version>1.0.0</version>
    <dependencies>
        <!-- JPA ํ•˜์ด๋ฒ„๋„ค์ดํŠธ -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.3.10.Final</version>
        </dependency>
        <!-- H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.1.214</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
    </dependencies>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
</project>

persistence.xml JPA ์„ค์ •ํ•˜๊ธฐ

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="hello">
        <properties>
            <!-- ํ•„์ˆ˜ ์†์„ฑ -->
            <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"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
            <!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
        </properties>
    </persistence-unit>
</persistence>

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

JPA ๋Š” ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ข…์†์ ์ด์ง€ ์•Š๋‹ค. ๊ฐ๊ฐ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” SQL ๋ฌธ๋ฒ•๊ณผ ํ•จ์ˆ˜๋Š” ์กฐ๊ธˆ์”ฉ ๋‹ค๋ฅด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, MySQL ์€ VARCHAR, Oracle ์€ VARCHAR2 ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ฆ‰, ๋ฐฉ์–ธ์€ SQL ํ‘œ์ค€์„ ์ง€ํ‚ค์ง€ ์•Š๋Š” ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ์˜ ๊ณ ์œ ํ•œ ๊ธฐ๋Šฅ์„ ์˜๋ฏธํ•œ๋‹ค. JPA ์—์„œ๋Š” hibernate.dialect ์†์„ฑ์— ์ง€์ •ํ•˜์—ฌ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฉ์–ธ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • H2 : org.hibernate.dialect.H2Dialect
  • Oracle 10g : org.hibernate.dialect.Oracle10gDialect
  • MySQL : org.hibernate.dialect.MySQL5InnoDBDialect

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


JPA ๊ตฌ๋™ ๋ฐฉ์‹

Persistence ๋ผ๋Š” ํด๋ž˜์Šค์—์„œ ์„ค์ • ํŒŒ์ผ์ธ persistence.xml ์„ ์ฝ์–ด์„œ EntityManagerFactory ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ดํ›„ EntityManagerFactory ์—์„œ EntityManager ๋ฅผ ์ฐ์–ด๋‚ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๊ณ  ๋งคํ•‘ํ•ด๋ณด์ž

package hellojpa;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity // JPA ๊ฐ€ ๊ด€๋ฆฌํ•  ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
public class Member {
    @Id // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ PK ์™€ ๋งคํ•‘ํ•œ๋‹ค.
    private Long id;
    private String name;
    // Getter, Setter โ€ฆ
}
create table Member (
    id   bigint not null,
    name varchar(255),
    primary key (id)
);

ํšŒ์› ๋“ฑ๋กํ•˜๊ธฐ

public static void main(String[] args) {
    // persistence.xml ์— ์žˆ๋Š” persistence-unit name ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();
    // code ์ž‘์„ฑ
    try {
        // ์˜์†
        Member member = em.find(Member.class, 150L);
        member.setName("ZZZZZ");
        tx.commit();
    } catch (Exception e) {
        tx.rollback();
    } finally {
        em.close();
    }
    emf.close();
}

์ฃผ์˜

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

JPQL

JPA ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค. ํŠน์ • ์—”ํ‹ฐํ‹ฐ ๊ฒ€์ƒ‰ ์‹œ์— em.find() ๋กœ ๋‹จ์ˆœํžˆ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ a.getB().getC() ์™€ ๊ฐ™์ด ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰์„ ํ†ตํ•ด ์กฐํšŒ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌธ์ œ๋Š” ์กฐ๊ฑด์ด ํฌํ•จ๋œ ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ๋ฅผ ์ „์†กํ•  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค. ์กฐ๊ฑด์ด ํฌํ•จ๋œ ๊ฒ€์ƒ‰์„ ํ•  ๋•Œ๋„ ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๊ฒ€์ƒ‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  DB ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ด๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ DB ์—์„œ ๋ถˆ๋Ÿฌ์˜ค๋ ค๋ฉด ๊ฒฐ๊ตญ ๊ฒ€์ƒ‰ ์กฐ๊ฑด์ด ํฌํ•จ๋œ SQL ์ด ํ•„์š”ํ•˜๋‹ค. ์ฆ‰, DB ์— ์ข…์†์ ์œผ๋กœ ์„ค๊ณ„๊ฐ€ ๋  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค๋Š” ์˜๋ฏธ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด JPA ๋Š” SQL ์„ ์ถ”์ƒํ™”ํ•œ JPQL ์ด๋ผ๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด ์ œ๊ณตํ•œ๋‹ค. SQL ์ด DB ์˜ ํ…Œ์ด๋ธ”์„ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฐ๋‹ค๋ฉด, JPQL ์€ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฐ๋‹ค. JPQL ์€ ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฉ์–ธ ์„ค์ •์— ๋งž์ถฐ์„œ ๊ฐ DB ์— ๋งž๊ฒŒ ๋ฒˆ์—ญํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ „์†กํ•œ๋‹ค. ์ด๋Š” JPQL ์ด SQL ์„ ์ถ”์ƒํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค SQL ์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์˜๋ฏธ์ด๊ธฐ๋„ ํ•˜๋‹ค.