엔티티 클래스

 

ERD

위의 설계대로 구현을 스프링 데이터 JPA를 사용하며 스프링 데이터 JPA에 대해 배워봅시다.

 

Member

package study.datajpa.entity;

import lombok.*;

import javax.persistence.*;

@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "username", "age"})
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private int age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;

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

    public Member(String username, int age, Team team) {
        this.username = username;
        this.age = age;
        changeTeam(team);
    }
}

Team

package study.datajpa.entity;

import lombok.*;

import javax.persistence.*;
import java.util.*;

@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "name"})
public class Team {
    @Id @GeneratedValue
    @Column(name = "team_id")
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    public Team(String name){
        this.name = name;
    }
}

 

위의 Entity에 해당하는 Repository의 CRUD 기능을 JPA를 통해 만들어 보도록 하겠습니다.

MemberJpaRepository

package study.datajpa.repository;

import org.springframework.stereotype.Repository;
import study.datajpa.entity.Member;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.*;

@Repository
public class MemberJpaRepository {
    @PersistenceContext //스프링 컨테이너가 엔티티 매니저를 가져다 줌
    private EntityManager em;

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

    public void delete(Member member){
        em.remove(member);
    }

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

    public Optional<Member> findById(Long id){
        Member member = em.find(Member.class, id);
        return Optional.ofNullable(member);
    }

    public long count() {
        return em.createQuery("select count(m) from Member m", Long.class)
                .getSingleResult();
    }

    public Member find(Long id){
        return em.find(Member.class ,id);
    }
}

TeamJpaRepository

package study.datajpa.repository;

import org.springframework.stereotype.Repository;
import study.datajpa.entity.Member;
import study.datajpa.entity.Team;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.*;

@Repository
public class TeamJpaRepository {
    @PersistenceContext
    private EntityManager em;

    public Team save(Team team){
        em.persist(team);
        return team;
    }

    public void delete(Team team){
        em.remove(team);
    }

    public List<Team> findAll() {
        return em.createQuery("select t from Team t", Team.class)
                .getResultList();
    }

    public Optional<Team> findById(Long id){
        Team team = em.find(Team.class, id);
        return Optional.ofNullable(team);
    }

    public long count() {
        return em.createQuery("select count(t) from Team t", Long.class)
                .getSingleResult();
    }

    public Team find(Long id){
        return em.find(Team.class ,id);
    }
}

 

Team과 Member에 대한 CRUD 기능을 갖춘 Repository를 만들었습니다. 위의 코드를 직접 쳐보시면 기본적인 CRUD 같은경우 클래스명과 관련된 것을 제외하고는 같은 코드인 것을 볼 수 있습니다. 즉, 반복적인 코드가 엔티티마다 발생하게 됩니다. 미리 결론부터 말하자면 이런 반복적인 코드를 줄여주는 기능을 하는 것이 바로 Spring Data JPA입니다.

 

 

 

공통 인터페이스 설정


 

Srping Data JPA가 제공하는 공통 인터페이스 기능을 쓰기 위한 준비 단계로 공통 인터페이스를 어떻게 설정하는 지 알아보자.

 

이전 JpaRepository를 상속받는 Spring Data JPA를 사용해 본 적이 있다. 해당 클래스는 interface이고 구현체가 없지만 기능이 정상적으로 작동한다. 이유는 무엇일까? 

 

실제 @Autowired한 Spring Data JPA를 사용한 interface를 출력해보면 아래와 같이 뜬다. interface에 Spring Data JPA가 구현클래스를 만들어서 대입했다고 보면 좋을 것 같다.

그렇기 때문에 구현체를 Spring Data JPA가 만들어서 injection을 해준 것이다.

 

그림으로 봐보자

Spring Data JPA가 애플리케이션 로딩 시점에 Spring Data JPA와 관련된 interface를 가지고 있으면 구현 클래스를 내부에서 만들어 냅니다. 즉, 애플리케이션 로딩 시점에 Spring Data JPA와 관련된 구현 클래스를 만들고 필요할 때 내부에서 injection을 해주는 것 입니다. 

참고로 Spring Data JPA를 상속받은 interface는 @Repository를 하지 않아도 됩니다. 

 

 

 

공통 인터페이스 분석


공통 인터페이스 구성

  • getOne(ID)
    • 엔티티를 프록시로 조회하는 기능. 레퍼런스를 가져올 때 쿼리문이 나가지 않고, 실제 엔티티에 접근할 때 쿼리문이 나간다.
    • Lazy.Loading을 생각하면 될 것 같다.
  • findAll(...)
    • 모든 엔티티를 조회한다. 정렬('Sort') 이나 페이징('Pageable') 조건을 파라미터로 제공할 수 있다.

 

 

 

출처

 

실전! 스프링 데이터 JPA - 인프런 | 강의

스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다.

www.inflearn.com

 

+ Recent posts