mojo's Blog

JPA 본문

Spring

JPA

_mojo_ 2022. 1. 14. 17:38

JPA

 

1. build.gradle 에서 다음과 같이 코드를 추가해준다.

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // new!
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

2. resources 폴더 아래의 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 // new!
spring.jpa.hibernate.ddl-auto=none // new!
  • JPA 인터페이스의 hibernate 만 사용한다고 보면 된다.

 

3. domain 폴더의 Member 클래스에서 다음과 같이 애너테이션을 추가해준다.

@Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    private String name;
    
    ...
}
  • @Entity : JPA가 관리하는 Entity가 되도록 하는 애너테이션이다.
  • @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) : DB에서 알아서 id값을 생성해주도록 하는 애너테이션이다.

 

4. repository 폴더 아래에 JpaMemberRepository 클래스를 만든 후 MemberRepository 인터페이스를 구현한다.

그리고 EntityManager 의 필드 em을 생성한 후 생성자를 통해 자동으로 객체가 투입되도록 구현한다.

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;
    }

    @Override
    public Member save(Member member) {
        return null;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.empty();
    }

    @Override
    public Optional<Member> findByName(String name) {
        return Optional.empty();
    }

    @Override
    public List<Member> findAll() {
        return null;
    }
}

 

5.  나머지 메서드들을 다음과 같이 채워준다.

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

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

    @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();
    }
  • save() : 필드 em의 persist 메서드를 인자 member를 통해 insert 를 해줌과 동시에 id 값을 자동으로 채워준다. (단 한줄이면 가능)
  • findById() : 파라미터로 받은 id 를 통해 필드 em의 find 메서드를 통해 객체 member를 얻고 그 값을 반환한다. 
  • findByName() : "select m from Member m where m.name = :name" 에서 Member 테이블을 m으로 여기며 select m 은 select * 과 동일하며 m.name = :name 은 파라미터로 받은 name을 setParameter() 를 통해 지정하여 찾는다.
  • findAll() : findByName() 메서드와 동일하며 파라미터 지정 없이 결과 리스트를 얻어오기만 하면 된다.

 

6. SpringConfig 에서 EntityManager의 필드 em을 생성하고 자동 할당을 위한 코드와 JpaMemberRepository 의 객체를 반환하기 위한 빈 등록을 해줘야 한다.

@Configuration
public class SpringConfig {

    private EntityManager em;

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

    ...

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

}

 

7. MemberService 클래스 상단에 @Transactional 애너테이션을 달아준다.

@Transactional
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    ...

}
  • @Transactional : 데이터를 저장하거나 변경할 때 항상 트랜젝션 애너테이션이 필요하다.

 

8. MemberServiceIntegrationTest 으로 이동해서 회원가입 메서드가 정상적으로 실행되는지 확인한다.

 

 

9. 회원가입 상단에 @Commit 애너테이션을 달아준 후 다시 실행해본다.

 

@Commit를 통해 rollback이 되지 않고 member 테이블에 저장되는 것을 알 수 있다.

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)

그리고 위와 같이 Member 클래스의 Long id 필드 위에 애너테이션을 달아줌으로써 ID가 자동으로 1부터 차례로 설정이 되는것을 알 수 있다.

 

10. 마지막으로 MemberServiceIntegrationTest 를 전부 실행시켜서 제대로 동작하는지 확인해본다.

 

 

스프링 데이터 JPA

 

스프링 부트와 JPA만 사용해도 개발 생산성이 많이 증가하고, 개발해야할 코드도 확연히 줄어든다.

여기에 스프링 데이터 JPA를 사용하면, 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스 만으로 개발을 완료할 수 있다. 

그리고 반복 개발해온 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공해준다.

스프링 부트와 JPA라는 기반 위에 스프링 데이터 JPA라는 환상적인 프레임워크를 더하면 개발이 정말 즐거워진다.

지금까지 조금이라도 단순하고 반복이라 생각했던 개발코드들이 확연하게 줄어든다.

따라서 개발자는 핵심 비즈니스 로직을 개발하는데 집중할 수 있다.

실무에서 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 이제 선택이 아니라 필수이다.

 

※ 스프링 데이터 JPA는 JPA를 편리하게 사용하도록 도와주는 기술이다.

      따라서 JPA를 먼저 학습한 후에 스프링 데이터 JPA를 학습해야 한다.

 

1. repository 폴더 아래에 SpringDataJpaMemberRepository 인터페이스를 형성한다.

다음과 같이 코드를 작성한다.

package hello.hellospring.repository;

import hello.hellospring.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);
}

 

2. 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);
    }
}

 

3. MemberServiceIntegrationTest 에서 회원가입이 제대로 동작하는지 실행해본다.

 

4. MemberServiceIntegrationTest 에서 모든 메서드가 제대로 동작하는지 실행해본다.

 

 

※ 스프링 데이터 JPA 제공 클래스

 

※ 스프링 데이터 JPA 제공 기능

  • 인터페이스를 통한 기본적인 CRUD
  • findByName(), findByEmail() 처럼 메서드 이름 만으로 조회 기능을 제공한다.
  • 페이징 기능을 자동으로 제공한다.

 

예를 들어서 위에서 작성했던 코드를 살펴보도록 한다.

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
    @Override
    Optional<Member> findByName(String name);
}
  • Optional<Member> findByName(String name) : 자동으로 select m from Member m where m.name = ? 이 적용되도록 오버라이딩 한 것이다.

'Spring' 카테고리의 다른 글

객체 지향 설계와 스프링  (0) 2022.01.20
AOP  (0) 2022.01.15
순수 JDBC & 스프링 JdbcTemplate  (0) 2022.01.13
회원 관리 예제 - 웹 MVC 개발  (0) 2022.01.13
스프링 빈과 의존관계  (0) 2022.01.12
Comments