[Spring] JPA 사용하기

김유진·2022년 11월 21일
0

Spring

목록 보기
12/12
post-custom-banner

JDBC에 관련된 내용도 JdbcTemplate을 이용하면 쉽게 코드를 훨씬 더 단축할 수 있다.
그러나 Jdbc를 사용하면서 여전히 개발자가 직접 쿼리문을 Spring에 입력해야한다는 단점이 존재한다.

그러한 sql 쿼리를 자동으로 처리해줄 수 있게끔 JPA를 사용해보자.

1. JPA 세팅

build.gradle로 들어가서 아래와 같이 세팅해줍니다.

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

요거를 추가해주세용!

그리고 application.properties에 들어가서 JPA와 관련된 설정을 진행합니다.

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

JPA에서 온 객체를 볼 수 있어야 하니까 sql 보는 설정을 true로 해두고, table 자동으로 만들어 주는 기능은 필요 없으니까 ddl-auto-none으로 설정합니다.

JPA가 관여하는 Entity를 생성해보자!

우리가 이전에 만들어 둔 Member 도메인을 Entity로 만들기 위해서 몇가지 세팅을 해야 한다.

package Inflearn_spring.studyspring.domain;
import javax.persistence.*;

@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에 관한 설정을 하게 되면 데이터베이스와 자동으로 연동하여서 원하는 값을 만들어내고 데이터를 이용할 수 있게 된다.

@Column(name = "username")

이렇게 Column을 설정하여 실제 db에 있는 column 값과 이어서 사용할 수 있다는 것도 얼마나 감격인지..!!!

JPA 관련 Repository를 생성해보자.

먼저 EntityManager 관련한 설정을 진행해야 한다.

public class JpaMemberRepository implements MemberRepository{

    private final EntityManager em;

    public JpaMemberRepository(EntityManager em) {
        this.em = em;
    }
...

JPA는 EntityManager를 기반으로 동작한다. Spring Boot가 이친구를 이용해서 현재 DB랑 자동으로 연결해주어서 쉽게 사용할 수 있도록 하는 것이다.

자 그럼 간단하게 save를 구현해보자.

public Member save(Member member) {
        em.persist(member);
        return member;
    }

오잉? 이렇게만 작성하면 끝이다. 여기서 persist는 영구적으로 저장한다는 뜻이다.
JPA manager가 자동으로 insert query를 만들어 DB에 member에 대한 정보를 집어넣어주는 것이다.

findByIdfindByname에 대한 것도 작성해보자.

@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 Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id);
        return Optional.ofNullable(member);
    }

이렇게 쿼리문 관련해서도 코드를 작성할 수 있다.

findAll에 대한 코드이다.

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

객체를 대상으로 query를 날려 sql로 번역해주는 것이다. 즉 Entity를 대상으로 쿼리를 날려서 멤버 엔티티 자체 m을 select하여 Member들을 찾아오는 것이다. 이미 매핑이 다 되어 있어서 한줄에 모든 친구들을 찾아올 수 있다.

2. JPA 사용시 주의점

JPA는 항상 트랜잭션이 있어야 한다. 그래서 Service객체에 아래와 같이 코드를 추가해주자.

@Transactional

데이터의 변경이 모두 Transaction안에서 실행이 되어야 한다.

3. SpringConfig 변경하기

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

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new JpaMemberRepository(em);
    }

자 여기서도 EntityManager를 생성해야 한다는 것을 잊으면 안된다... 한번 돌려보자!

4. Spring Data JPA로 개발 생산성을 높여보자.

repository에 다음 이름을 가진 Interface를 만들어보자.
SpringDataJpaMemberRepository

package Inflearn_spring.studyspring.repository;

import Inflearn_spring.studyspring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

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

이렇게 만들게 되면 SpringDataJpaMember가 extend받은 목록을 보고 지가 자동으로 구현체를 만들어서 Spring Bean에 등록하게 됩니다. 그래서 이걸 SpringConfig에서 가져다가 쓸 수 있는 것이다.

@Configuration
public class SpringConfig {
    private final MemberRepository memberRepository;

    @Autowired
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository);
    }

//    @Bean
//    public MemberRepository memberRepository() {
        //return new JpaMemberRepository(em);
        //return new JdbcTemplateMemberRepository(dataSource);
        //return new JdbcMemberRepository(dataSource);
        //return new MemoryMemberRepository();
//    }
}

짜잔~ 이렇게 memberRepository에 대해서 만들 수 있는데 그 이유는 SpringDataJpaMemberRepository에서 Spring data에 대한 구현체를 기술을 가지고 만들어내, Spring Bean에 등록하여 위와 같이 service에 injection을 시켜놓을 수 있는 것이다. 즉 스프링 데이터 JPA가 SpringDataJpaMemberRepository 를 Bean으로 자동 등록해준것이다.

아니 그럼 우리는 savefindAll 같은 것은 구현 안했는데 그건 어딨는거누?
찾아본다면 .. JpaRepository에서 자동으로 제공하고 있다.

오메 진짜네. 나는 작성한적이 없는 코드들이 막막 제공되고 있따! CRUD 레포지토리도 있어서 save도 자동으로 해주는 것이다.

구조를 그림으로 나타내면 위와 같다. 각종 기본 메서드들을 Spring Data JPA에서 제공하는 것을 알 수 있다.

그러나 자동적으로 제공하지 않았던 findByName이 있다. 이런건 공동적으로 제공할 수 있는 인터페이스가 아니네! 그래서 우리는 아래와 같이 작성을 해주었고..

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

findBy라는 규칙만 지킨다면 알아서 JPQL을 select m from Member m where m.name =?으로 작성을 해주는 것이다.
findByNameAndId(String name Long id)이렇게 인터페이스 이름만으로도 개발을 진행할 수 있다.
이렇게 단순한 로직은 인터페이스로 이용하도록 하자!

post-custom-banner

0개의 댓글