김영한님의 '스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술'을 듣고 적은 글입니다.
JDBC -> JDBC TEMPLATE 로 옮겨져 오면서 반복적인 코드를 줄이긴 했으나 SQL은 결국 개발자가 작성해야했다. 하지만 JPA를 사용하면 SQL 쿼리도 자동으로 처리해준다. JPA에 집어 넣으면 SQL을 DB에 날리고 알아서 처리해준다.
JPA : 기존의 반복 코드, 기본적인 SQL도 직접 만들어 실행해준다.
장점 )
JPA 사용을 위해 build.gradle 파일에 JPA, h2 데이터베이스 관련 라이브러리를 추가해준다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
//implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
spring-boot-starter-data-jpa 는 내부에 jdbc 관련 라이브러리를 포함한다. 그래서 jdbc를 주석 처리한 것이다.
그리고 resources > application.properties 에 JPA 설정을 추가해준다.
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
ddl-auto는 JPA가 테이블을 자동으로 생성하는 기능을 제공한다. 지금은 비활성화 시켰지만 활성화 시켜주기 위해서는 create로 바꿔주면 된다.
그리고 JPA 엔티티 매핑을 위해 이전에 만들어둔 Member.java 파일을 열어 코드를 조금 수정해준다.
package hello.hellospring.domain;
import jakarta.persistence.*;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
// 아이덴티티 : DB에서 ID 자동 생성
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;
}
}
그리고 JpaMemberRepository.java 파일을 만들어준다.
package hello.hellospring.repository;
import hello.hellospring.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;
}
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();
}
}
쿼리문을 가만히 보면 우리가 여태 사용한 쿼리문과 조금 다름을 알아챌 수 있다. 보통 쿼리는 테이블 단위로 쿼리를 날리는데, 이 쿼리문은 객체 단위로 대상으로 쿼리를 날리는 것이다. 이를 JPQL 이라고 한다.
JPQL :
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다. SQL과 문법은 유사하다.
JPQL은 엔티티 객체를 대상으로 쿼리를 질의하고 SQL은 데이터베이스 테이블을 대상으로 쿼리를 질의한다.
package hello.hellospring;
import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
@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 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);
}
}
그리고 순수 JDBC에서 만든 테스트 케이스를 작성하면 성공적으로 실행되는 것을 알 수 있다.