TIL - JAVA spring DAY 16 (JPA 구현 完)

jihan kong·2022년 1월 26일
0

JAVA spring 입문

목록 보기
19/20
post-thumbnail

JPA를 구현하는 과정에 앞서 환경을 세팅해주는 작업을 하고 있다. 다음으로는 도메인을 건드려보자. domain package에 Member.java class를 열고 다음의 내용으로 수정한다.

Member.java

package hello.hellospring.domain;

import javax.persistence.*;

@Entity // 1.
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 2.
    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;
    }
}
  1. @Entity annotation을 붙여주면 이제부터 JPA가 관리하는 개체가 되는 것이다.

  2. @Id, @GeneratedValue(strategy = GenerationType.IDENTITY)
    PK (primary key) 를 나타내기 위해 @Id 를 붙여준 것이다.
    PK는 현재 우리가 DB에서 값을 생성해주고 있다.
    그런데, 전에 DB에서 insert문을 작성할 때, (insert into member(name) values("spring1");) ID가 자동으로 할당되는 모습을 h2 콘솔을 통해 본 적이 있다. 이처럼 DB에 값을 넣으면 DB가 자동으로 생성해주는것을 IDENTITY 전략이라고 하며, @GeneratedValue는 이것을 표현해주고 있다.

위와 같은 annotation을 통해 DB와 매핑을 하면 이 정보를 토대로 JPA가 Insert, Select, Delete 문 등의 SQL문들을 자동으로 작성해 줄 수 있는 것이다.

이제, JPA 기반으로 움직일 Repository를 생성해보자.

JpaMemberRepository.class

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;		// 1.

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

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

    @Override
    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id);   // 3.
        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)						// 4.
                .setParameter("name", name)
                .getResultList();

        return result.stream().findAny();
    }

    @Override
    public List<Member> findAll() {
        List<Member> result = em.createQuery("select m from Member m", Member.class)
                .getResultList();
        return result;
    }
}
  1. JPA는 EntityManager 라는 메소드로 동작한다. 따라서 이를 토대로 생성자까지 만든 내용이다. 우리가 전에 gradle에서 'org.springframework.boot:spring-boot-starter-data-jpa' 를 implement 해주었다. 그렇게하면, spring boot가 자동으로 EntityManager를 생성해준다. 우리는 이를 injection 받기만 하면 된다.

  2. 이렇게하면 JPA가 Insert query를 만들어서 DB에 저장하고, member 에 setId까지 해준다.

  3. Id로 찾는 findById 메소드의 경우, em.find 를 사용하고 인자값으로 조회할 타입과 Pk값을 넣어주기만 하면 된다. (값이 없을수도 있으므로 Optional로 감싼다.)

  4. 이름으로 찾는 findByName 메소드는 Jpql이라는 어떤 특별한 객체지향 쿼리언어를 사용해야하는데, 보통은 테이블 대상으로 SQL을 사용하지만 개체(Entity)를 대상으로 쿼리를 사용하는 것을 의미한다.
    보통 SQL문이라고 하면 "select * from Member", "select m.Id Member" 와 같은 식으로 작성하는게 일반적이다.
    또 이를 매핑하는 작업도 필요하다. 그러나 Jpql로 작성하게 되면, "select m from Member m" 와 같이 Member Entity 자체를 select하는 것을 볼 수 있다. 매핑도 다 되어있다.

  5. 4번과 같은 방식으로 구현

마지막으로 JPA를 사용하려면 항상 주의해야할 것이 데이터를 저장하고 변경하는 것이므로 Transactional이 동작해야한다. 따라서 MemberService.class 에 다음과 같이 annotation을 추가해준다.

@Transactional
public class MemberService {

    private final MemberRepository memberRepository;

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

Config...

드디어 마지막... 조립 단계이다.
service package에 있는 Spring config.class 를 살펴보자.

private DataSource dataSource;

	@AutoWired
    public SpringConfig(DataSource dataSource) { 
    	this.dataSource = dataSource; 
    }

위와 같이 기존에 dataSource 로 받던 내용을 이제는 지우고, EntitiyManager 로 받기 위해 다음과 같이 작성한다.

private EntityManager em;

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

그 후, memberRepository() 메소드에 다음과 같이 JpaMemberRepository(em) 를 추가해준다.

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

(지금까지 memberRepository의 역사...)

대망의 통합테스트 시간이다. 과연...?

성공! 테스트 결과를 자세히 살펴보면....

위와 같이 hibernate 구현체가 select와 insert query를 날리고 있는 것을 볼 수 있다.

이렇게 JPA에 대한 학습을 마쳤다. 다른 것보다 기술들이 발전해나가는 패러다임이 나에게 어떠한 시사점을 던져주었다. 모든 사람들이 순수하게 그리고 당연하게 오리지널 Jdbc 코드로 DB와 연동하여 개발할 때, 어떤 사람은 불편함을 느꼈을 것이다. 그리고 그러한, 불편함을 토대로 spring Jdbc Template를 새롭게 탄생시켰을 것이다. JPA 프레임워크 또한, 그러한 필요성에 의해 발생되었으리라..

무엇이든 개발할 때 있어서 불편함을 느끼는 것이 새로운 발명의 시작임을 느끼고 항상 불편해지는 마인드셋과 그것을 어떻게하면 개선할까 노력하는 태도를 가지는 것이 중요함을 깨닫는다.

profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글