본문 바로가기
Spring_inflearn/입문

[Spring] inflearn 스프링 입문 - 스프링 DB 접근 기술 5

by clolee 2022. 9. 15.

JPA

JDBC -> JdbcTemplate : 반복적인 코드 줄음. but sql 쿼리는 직접 작성해야 함.

JPA 기술을 사용하면 sql 쿼리도 JPA가 자동으로 처리. 개발 생산성 높임.

객체를 JPA에 넣으면 JPA가 중간에서 DB에 sql을 날리고 데이터를 db에서 가져오는 것 처리.

sql, 데이터 중심 설계에서 객체 중심 설계로 전환.

 

build.gradle dependency 추가. jpa관련 라이브러리 추가.

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

resources/application.properties

JPA관련 설정 추가

 

jpa가 날리는 sql 볼 수 있음

spring.jpa.show-sql=true

 

jpa를 사용하면 객체를 보고 테이블 만듦. 테이블 만든 것을 사용할 것이므로 테이블 자동 생성 끄고 시작.

none을 create로 바꾸면 create table 까지 자동으로 만들어줌

spring.jpa.hibernate.ddl-auto=none

 

jpa, hibernate 라이브러리 추가 확인

jpa 는 인터페이스(자바 진영의 표준 인터페이스), hibernate 는 구현체.

jpa는 객체 + ORM (Object-relational mapping) 기술

ORM : 객체 object와 relational database table을 mapping 한다. mapping은 어노테이션으로 한다.

 

jpa 사용 위해 @Entity 맵핑. jpa가 관리하는 entity.

pk 맵핑 @Id

db가 id 자동으로 생성. 아이덴티티 전략 @GeneratedValue(strategy = GenerationType.IDENTITY)

 

db의 컬럼명이 username이면 name위에 @Colunm(name = "username") 으로 하면 username과 맵핑

 

어노테이션들을 가지고 DB와 맵핑. 이 정보를 가지고 sql문을 만든다

package hello.hellospring.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

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

 

 

리포지토리 만들기

 

jpa는 EntityManager로 동작.

build.gradle에서 data-jpa 라이브러리 받음. 스프링부트가 자동으로 EntityManager 생성. 만들어진 EntityManager를 injection받음

EntityManager는 dataSource를 내부적으로 들고 있어 db와 통신을 내부에서 처리

jpa를 쓰려면 EntityManager를 주입받아야 함.

 

save()

jpa가 insert 쿼리 만들어 db에 넣고 id까지 member에서 setId 해줌

 

findById()

em.find(조회할 타입, 식별자 pk)

 

findAll()

jpql 객체지향 쿼리 언어 사용

테이블 대상이 아닌 엔티티(객체) 대상으로 쿼리 날림. 이것이 sql로 번역이 됨

em.createQuery("select m from Member m", Member.class)
                .getResultList();

Member 엔티티를 조회해서 Member 엔티티 자체를 select. Member.class로 조회. 맵핑은 이미 되어있음

 

findByName()

jpql 객체지향 쿼리 언어 사용

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) {
        // persist : 영속하다, 영구저장
        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();
    }
}

 

저장, 조회, 업데이트, 단 건을 찾을 때- jpql 필요 없음

findByName, pk기반이 아닌 것, 여러 개 리스트 돌릴 때 - jpql 작성

 

jpa기술을 스프링에 한번 감싸서 제공하는 기술 : 스프링 데이터 jpa

스프링 데이터 jpa를 사용하면 findByName(), findAll()에도 jpql 사용하지 않아도 됨.

 

jpa 사용하려면 항상 트랜잭션이 있어야 함. 데이터를 저장하거나 변경할 때.

모든 데이터 변경이 트랜잭션 안에서 실행되어야 함.

src/main/java/hello/hellospring/service/MemberService.java

MemberService 위 아니면 join 위에 @Transactional (회원가입 시에만 필요하므로)

@Transactional
public class MemberService {
	private MemberRepository memberRepository;

	public MemberService(MemberRepository memberRepository) {

		this.memberRepository = memberRepository;
	}

	/**
	 * 회원 가입
	 */
    // @Transactional
	public Long join(Member member) {
		// 같은 이름이 있는 중복 회원x
		validateDuplicateMember(member); // 중복 회원 검증

		memberRepository.save(member);
		return member.getId();
	}

 

src/main/java/hello/hellospring/SpringConfig.java

EntityManager 필요

package hello.hellospring;

import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;

@Configuration
public class SpringConfig {

//    private DataSource dataSource;
//
//    @Autowired
//    public SpringConfig(DataSource dataSource) {
//        this.dataSource = dataSource;
//    }

    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 MemoryMemberRepository();
//        return new JdbcMemberRepository(dataSource);
//        return new JdbcTemplateMemberRepository(dataSource);
        return new JpaMemberRepository(em);
    }
}

 

src/test/java/hello/hellospring/service/MemberServiceIntegrationTest.java

스프링 통합테스트 실행

jpa 세팅하면 hibernate 구현체가 사용됨. select, insert 쿼리가 db에 적용됨

select : validation 에서 하나 가져와 find

insert : db에 저장

@Commit으로 DB 확인

	@Test
	@Commit
	void 회원가입() {
		// given 상황이 주어졌을 때
		Member member = new Member();
		member.setName("spring");

		// when 이걸 실행 했을 때
		Long saveId = memberService.join(member);

		// then 결과가 이렇게 나와야 함
		Member findMember = memberService.findOne(saveId).get();
		assertThat(member.getName()).isEqualTo(findMember.getName());
	}

위의 test join 에서 setName에 name을 spring100으로 변경해 다시 실행

@Commit 지우고 전체 테스트 실행해보기

테스트 모두 성공

 

 

command + option + n : inline 단축키

control + t : 검색

 

댓글