[Java Spring 무작정 따라하기] JPA

khj·2024년 8월 3일
post-thumbnail

✨ JPA Entity 생성

JDBC에서 Jdbc Template으로 수정하니 개발해야 할 코드는 확 줄었다.
하지만 아직까지 쿼리를 코드에서 제거하지 못했다.
JPA를 사용하면 쿼리도 자동으로 처리할 수 있어 쿼리를 작성할 필요가 없다고 한다.

먼저 JPA를 사용하기 위해서 라이브러리를 추가해야 한다.

// hello-spring/build.gradle
...
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    ...
}
...

그리고 설정을 추가한다.

# main/application.properties
...
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

hibernate.ddl-auto를 create로 작성하면 객체를 보고 테이블을 자동으로 생성해준다고 한다.
하지만 이미 테이블이 만들어져 있는 상태이므로 none으로 작성하고 진행했다.

JPA는 ORM을 통해 객체를 관계형 DB의 테이블로 맵핑하는데, 이를 위한 Entity 클래스가 필요하다.
기존에 작성했던 Member.java를 살짝 수정했다.

// domain/Member.java
package hello.hello_spring.domain;

import jakarta.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 어노테이션을 사용하면 Member를 JPA에서 관리하는 Entity로 인식한다.
@Id 어노테이션을 사용하면 pk로 지정된다.
그리고 @GeneratedValue 어노테이션으로 GenerationType.IDENTITY을 작성하면 DB에서 이 값을 자동으로 생성해준다.

name은 테이블에서도 name으로 생성되어 있으니 추가적인 어노테이션은 필요없지만 만약 다르다면 @Column(name = "테이블의 Column명") 으로 어노테이션을 작성해주면 된다.

✨ 리포지토리 생성

Entity 클래스를 작성했다면 Jdbc Template과 마찬가지로 리포지토리를 생성해주면 된다.

// repository/JpaMemberRepository.java
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);
        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();
    }
}

JPA에서는 EntityManager라는 것이 필요하다고 한다.
EntityManager는 Entity와 관련된 DB 작업을 수행한다고 하는데 CRUD 작업도 포함이다.

save 메서드는 매우 간단하다.
EntityManager.persist 메서드를 사용하면 DB 저장까지 알아서 처리해준다고 한다.

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

findById는 EntityManager.find 메서드를 사용하면 된다.

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

find로 객체를 찾지 못한다면 null값이 반환되니 Optional을 사용해 반환했다.

findByName은 EntityManager.find가 아닌 EntityManager.createQuery 메서드를 사용해 JPQL(JPA Query Language) 쿼리를 작성했다.

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

findAll 메서드도 마찬가지로 JPQL 쿼리를 작성했다.

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

JPA는 트랙잭션 기반의 API이기 때문에 DB와의 모든 상호작용은 트랜잭션 내에서 이루어져야 한다.
@Transactional 어노테이션을 사용해 이를 지정할 수 있다고 한다.

// service/MemberService.java
import org.springframework.transaction.annotation.Transactional;

@Transactional
public class MemberService {
...
}

🚩 JPA 테스트

작성한 JPA 리포지토리를 테스트해보기 위해서 SpringConfig를 수정했다.

// hello.hello_spring/SpringConfig.java
...
import hello.hello_spring.repository.JpaMemberRepository;
import jakarta.persistence.EntityManager;

@Configuration
public class SpringConfig {

    private EntityManager em;

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

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

EntityManager를 선언 및 초기화하고 이를 JpaMemberRepository에 전달했다.

테스트는 문제 없이 끝난 것을 볼 수 있었다.

💡 쿼리?

앞서 설명하면서 쿼리를 작성할 필요가 없다고 작성해놓고 쿼리를 사용했는데 JPA를 스프링으로 한번 감싸서 제공하는 기술을 사용하면 쿼리를 작성할 필요가 없다고 한다.
이는 다음 글에서 진행할 예정이다.

profile
Spring, Django 개발 블로그

0개의 댓글