스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 by 김영한
dependencies {
//  implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
spring-boot-starter-data-jpa 는 내부에 jdbc 관련 라이브러리를 포함한다. 따라서 jdbc는 제거해도 된다.
resources/application.properties
...
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
show-sql : JPA가 생성하는 SQL을 출력한다.ddl-auto : JPA는 테이블을 자동으로 생성하는 기능을 제공하는데 none 를 사용하면 해당 기능을 끈다. H2 DB를 설치하고 직접 테이블 생성을 해주었기 때문에 none으로 설정하였다.create 를 사용하면 엔티티 정보를 바탕으로 테이블도 직접 생성해준다.@Entity
public class Member {
   @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
   ...
}
@GeneratedValue(strategy = GenerationType.IDENTITY) : 기본키 설정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;
   }
 
   public Member save(Member member) {
     em.persist(member);
     return member;
   }
 
   public Optional<Member> findById(Long id) {
     Member member = em.find(Member.class, id);
     return Optional.ofNullable(member);
   }
 
   public List<Member> findAll() {
     return em.createQuery("select m from Member m", Member.class)
           .getResultList();
 }
 
   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();
   }
 
}
import org.springframework.transaction.annotation.Transactional
@Transactional
public class MemberService {...}
org.springframework.transaction.annotation.Transactional 를 사용하자.주의! JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
@Configuration
public class SpringConfig {
   private final DataSource dataSource;
   private final EntityManager em;
 
   public SpringConfig(DataSource dataSource, EntityManager em) {
     this.dataSource = dataSource;
     this.em = em;
   }
 
   @Bean
   public MemberRepository memberRepository() {
//   return new MemoryMemberRepository();
//   return new JdbcMemberRepository(dataSource);
//   return new JdbcTemplateMemberRepository(dataSource);
     return new JpaMemberRepository(em);
   }
}
findByName() , findByEmail() 처럼 메서드 이름 만으로 조회 기능 제공참고: 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고, 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용하면 된다. Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성할 수 있고, 동적 쿼리도 편리하게 작성할 수 있다. 이 조합으로 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브 쿼리를 사용하거나, 앞서 학습한 스프링 JdbcTemplate를 사용하면 된다.
스프링 데이터 JPA 설정은 위의 JPA 설정을 그대로 사용한다.
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 {
   Optional<Member> findByName(String name);
}
package hello.hellospring;
import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
   private final MemberRepository memberRepository;
   
   public SpringConfig(MemberRepository memberRepository) {
     this.memberRepository = memberRepository;
   }
   
   @Bean
   public MemberService memberService() {
     return new MemberService(memberRepository);
   }
}
SpringDataJpaMemberRepository 를 스프링 빈으로 자동 등록해준다.