๋ฐฑ์—”๋“œ(Back-End) ๊ฐœ๋ฐœ/SpringBoot

[SpringBoot] 6.5_JPA

rabo93 2025. 2. 26. 20:19
JPA(์ž๋ฐ” ํผ์‹œ์Šคํ„ด์Šค API) vs MyBatis
  JPA (์ž๋ฐ” ํผ์‹œ์Šคํ„ด์Šค API) MyBatis (๋งˆ์ด๋ฐ”ํ‹ฐ์Šค)
๊ณตํ†ต์  Java ๊ธฐ๋ฐ˜์˜ Spring ๋˜๋Š” SpringBoot์—์„œ ์‚ฌ์šฉ
DB์—ฐ๋™ ๊ธฐ์ˆ 
DB๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฒˆ๊ฑฐ๋กœ์šด ๋ฐ˜๋ณต์ž‘์—…์„ ์—†์• ์คŒ
์ฐจ์ด์  ORM(Object Relation Mapping) ๊ธฐ์ˆ 
: ๊ฐ์ฒด์™€ DB์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋งคํ•‘
SQL๋ฌธ๊นŒ์ง€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ,
DB ๋ฐ์ดํ„ฐ์™€ Java ๊ฐ์ฒด๋ฅผ ๋งคํ•‘์‹œ์ผœ์คŒ์œผ๋กœ์จ
MyBatis๋ณด๋‹ค ํŽธ๋ฆฌํ•˜์ง€๋งŒ ๋ฐฐ์šฐ๊ธฐ ์–ด๋ ค
๊ฐ์ฒด ์ง€ํ–ฅ์  ๊ฐœ๋ฐœ์— ์ค‘์ 
๊ฐ„๋‹จํ•œ ๋งคํ•‘ ๋ฐ ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ธ ์ ‘๊ทผ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์„ ํƒ
SQL Mapper ๊ธฐ์ˆ 
: ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž‘์„ฑํ•œ SQL ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด์— ๋งคํ•‘
XML ํŒŒ์ผ๋กœ SQL๋ฌธ Java์™€ ๋ถ„๋ฆฌ
SQL๊ฐœ๋ฐœ๊ณผ ์œ ์ง€๋ณด์ˆ˜์— ์šฉ์ด
๋™์  SQL ์ƒ์„ฑ ๊ธฐ๋Šฅ
์ „์ž์ •๋ถ€ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์‚ฌ์šฉ
๋ณต์žกํ•œ ์ฟผ๋ฆฌ์™€ SQL ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์„ ํƒ

 


JPA ํ™˜๊ฒฝ ์„ค์ •
  • 'build.gradle'์— JPA ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€
dependencies {
    ...์ƒ๋žต
//  implementation 'org.springframework.boot:spring-boot-starter-jdbc' //jdbc๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ง€์›Œ๋„ ๋จ
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
  	...์ƒ๋žต
}

 

  • 'application.properties'์— JPA ์„ค์ • ์ถ”๊ฐ€
#JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

* ddl-auto : JPA๋Š” ํ…Œ์ด๋ธ”์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ none ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๋ˆ๋‹ค. create ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ…Œ์ด๋ธ”๋„ ์ง์ ‘ ์ƒ์„ฑํ•ด์ค€๋‹ค.

 


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

๊ธฐ์กด Member ํด๋ž˜์Šค์— @Entity ์–ด๋…ธํ…Œ์ด์…˜ ๋‹ฌ์•„์คŒ

package hello.hello_spring.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity //JPA๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) //PK ์„ค์ •
    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;
    }

}

 

  • JPA ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ
package hello.hello_spring.repository;

import hello.hello_spring.domain.Member;
import jakarta.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;
    }

    @Override
    public Member save(Member member) {
        em.persist(member); //.persist()ํ•˜๋ฉด JPA๊ฐ€ ์ž๋™์œผ๋กœ INSERT๋ฌธ ์ƒ์„ฑํ•˜๊ณ  member์— setId๊นŒ์ง€ ํ•ด์คŒ.
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id); //.find()ํ•˜๋ฉด JPA๊ฐ€ ์ž๋™์œผ๋กœ SELECT๋ฌธ ์ƒ์„ฑํ•˜์—ฌ ์กฐํšŒํ•จ.
        return Optional.ofNullable(member); //์กฐํšŒํ–ˆ์„๋•Œ null๊ฐ’์ด ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ Optional์— ๊ฐ์‹ผ๋‹ค.
    }

    @Override
    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();
    }

    @Override
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
}

 

JPA์˜ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ em.persist();๋“ฑ๋“ฑ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ถ”ํ›„์— ๊ณต๋ถ€ํ•˜๋„๋ก ํ•˜์žโœ… 

 

  • Service ๊ณ„์ธต์—๋Š” @Transaction ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

=> JPA๋ฅผ ํ†ตํ•œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค.

 

 

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

import hello.hello_spring.repository.*;
import hello.hello_spring.service.MemberService;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class SpringConfig {

    private EntityManager em;
    @Autowired
    public SpringConfig(EntityManager em) {
        this.em = em;
    }

    /*
        private DataSource dataSource;
        @Autowired
        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); //JDBC ์—ฐ๊ฒฐ ํ›„ Configuration ๋ถ€๋ถ„๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋จ.
//        return new JdbcTemplateMemberRepository(dataSource); //JdbcTemplate
        return new JpaMemberRepository(em); //JPA
    }

}

 

 

์ฐธ๊ณ ) ์Šคํ”„๋ง๋ถ€ํŠธ์™€ JPA  ํ•™์Šตํ•œ ํ›„ '์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA ' ์‚ฌ์šฉํ•˜๋ฉด ๋”์šฑ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.