[작성일: 2023. 05. 22]
JPA
- JPA는 기존의 반복 코드는 물론이고 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
- JAP를 사용하면 SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있다.
- JAP를 사용하면 개발 생산성을 크게 높일 수 있다.
Build.gradle
- JDBC 관련 코드는 주석처리 해주고 JPA, h2 데이터베이스 관련 라이브러리를 추가한다.
- spring-boot-starter-data-jpa는 내부에 jdbc 관련 라이브러리를 포함하기 때문에 jdbc는 제거해도 상관없다.
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'
implementation 'org.junit.jupiter:junit-jupiter:5.8.1'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}
application.properties
- 스프링부트에 JPA 설정 추가
- 스프링부트 2.4부터는 spring.datasource.username=sa를 추가하지 않으면 오류가 발생한다.
- show-sql : JAP가 생성하는 SQL을 출력한다.
- ddl-auto : JAP는 테이블을 자동으로 생성하는 기능을 제공하는데 none을 사용하면 해당 기능을 끈다.
- create를 사용하면 엔티티 정보를 바탕으로 테이블도 직접 생성해준다.
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
Member.class
- JPA 엔티티 매핑을 위해 Member 클래스에서 Entity 애노테이션과 Id, GeneratedValue 애노테이션 추가
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 임의의 값(시스템이 저장하는 id)
private String name; // 이름
...
}
JpaMemberRepository
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();
}
}
MemberService
- 서비스 계층에 트랜잭션 추가
- org.springfraework.transaction.annotation.Transactional 사용
- 스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을 커밋한다. 만약 런타임 예외가 발생하면 롤백한다.
- JAP를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class MemberService {
...
}
SpringConfig
- JPA를 사용하도록 스프링 설정 변경
@Configuration
public class SpringConfig {
// private final DataSource dataSource;
//
// public SpringConfig(DataSource dataSource) {
// this.dataSource = dataSource;
// }
private final EntityManager em;
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);
}
}
스프링 데이터 JPA
- 스프링부트와 JPA만 사용해도 개발 생산성이 증가하고 단순하고 반복이라고 생각했던 개발해야 할 코드들이 확연이 줄어든다.
- 여기에 스프링데이터 JPA를 사용하면 기존의 한계를 넘어 리포지토리에 구현 클래스 없이 인터페이스 만으로 개발을 완료할 수 있다.
- 또 반복 개발해온 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공한다.
- 개발자는 핵심 비즈니스 로직을 개발하는데 집중할 수 있으며 실무에서 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 선택이 아닌 필수항목이다.
SpringDataJpaMemberRepository
- 인터페이스 생성
- 인터페이스가 인터페이스를 받을 땐 implements가 아닌 extends 사용
public interface SpringJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository{
// 인터페이스가 인터페이스를 받을 땐 extends 사용
@Override
Optional<Member> findByName(String name);
}
SpringConfig
- 스프링 데이터 JPA 회원 리포지토리를 사용하도록 스프링 설정 변경해주기
- 스프링 데이터 JPA가 SpringDataJpaMemberRepository를 스프링 빈으로 자동 등록해준다.
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@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);
// }
}
스프링 데이터 JPA 제공 클래스/기능
- 스프링 데이터 JPA에서 findAll, save, delete 등 기본적으로 공통화해서 제공해준다.
- 인터페이스를 통한 기본적인 CURD 제공
- findByName(), findByEmail()처럼 메서드 이름 만으로 조회 기능 제공
- 페이징 기능 자동 제공
- 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용한다.
- Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성할 수 있고, 동적 쿼리도 편리하게 작성할 수 있다.
- 이 조합으로 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브 쿼리를 사용하거나 스프링 JdbcTemplate을 사용하면 된다.
🐣 출처: 인프런 김영한님 강의
이 글은 인프런의 김영한님 스프링 강의를 보고 작성한 글입니다.
강의를 들으면서 정리한 글이므로 틀린 내용이나 오타가 있을 수 있습니다.