개발새발 블로그

김영한T 스프링 입문강의 - [섹션6] 스프링 DB 접근 기술 (JPA, 스프링 데이터 JPA) 본문

백/spring

김영한T 스프링 입문강의 - [섹션6] 스프링 DB 접근 기술 (JPA, 스프링 데이터 JPA)

복지희 2023. 11. 24. 15:37

<JPA>

JPA는 SQL도 직접 만들어서 실행해준다.

build.gradle에 JPA관련 라이브러리를 추가해준다.

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

jpa가 내부에 jdbc 라이브러리를 포함하고 있어서, jdbc는 제거해도 된다.

 

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

application.properties에 JPA설정을 추가해준다.

show-sql은 JPA가 날리는 SQL을 볼 수 있고,

ddl-auto는 테이블을 자동으로 생성하는 기능을 제공하는데, 위에서 h2 db에 테이블을 이미 생성해주었으므로, 우리는 해당 기능을 꺼도 된다.

-> none이 아닌 create를 사용하면 테이블을 자동으로 생성해준다.

 

@Entity
public class Member {
    ...
}

도메인 클래스에 @Entity 애노테이션을 추가해주면, 앞으로 JPA가 관리한다는 뜻이다.

@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;    //임의의 값. 데이터 구분위해 시스템이 정해주는
    
    @Column(name="username")
    private String name;
	...

현재 id는 pk(primary key)이다.

회원이 입력하는 값으로 들어가는게 아니라, db에서 데이터구분을 위해 자동으로 생성된다.

그래서 id에는 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) 이런 애노테이션을 적어주어야하고,

만약, 테이블에 저장되어있는 열의 이름이 username이라면 그에 맞춰주기 위해 @Column(name="username") 애노테이션을 추가한다.

 

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) {
        //pk는 위에서 한것처럼 바로 조회가 가능하지만, 나머지는 불가능. 객체쿼리를 사용해서 처리해야함
        //Member member = em.find(Member.class, name); 불가능 (pk가 아니라서)
        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();
    }
}

EntityManager를 사용해서 처리해준다.

그리고 pk를 사용해서 객체를 불러올 경우에만  Member member = em.find(Member.class, id); 를 사용할 수 있고,

아닐 경우에는 em.createQuery("select m from Member m where m.name = :name", Member.class) 이렇게 객체를 불러오는 쿼리문을 사용해줘야 한다.

 

 


<스프링 데이터 JPA>

JPA를 사용하면 리포지토리 구현을 하지 않고도 개발을 완료할 수 있다.

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
    @Override
    Optional<Member> findByName(String name);
}

 

JpaRepository를 상속받기만해도 알아서 구현체를 자동으로 만들어준다. 기본적인 CRUD는 다 제공을 해준다.

그리고 스프링빈에도 알아서 등록을 해준다.

 

JpaRepository가 못만드는게 있다. 누구나 다 만드는 공통된 것이 아니라, findByName 같은 이름으로 찾거나, 이메일로 찾고 싶은 등 우리의 도메인으로 사용하는 것들은 직접 구현을 해줘야한다.

 

findByName을 실행하게되면 JPQL로 select m from Member m where m.name = ? 을 만들어줄건데,

Optional<Member> findByNameAndId(String name, Long id);

이렇게 쓰면 Name과 Id로 찾아주는 로직이다. 이렇게 이름만 규칙에 맞게 잘 써줘도 JpaRepository가 알아서 구현해준다!!

 

복잡한 쿼리같은 경우는 직접 구현까지 해줘야한다.

Querydsl 이라는 라이브러리를 사용하면 되는데, 

 

@Configuration
public class SpringConfig {

    private final MemberRepository memberRepository;

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

    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository);
    }

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