자바 프로젝트에서 생성한 데이터들을 DB와 연동하고자 한다.
spring에서 제공하는 JPA 방식을 사용하여
쿼리문 없이 깔끔하게 코드를 작성하는 방법을 알아보고자 한다.
DB 연동은, 같은 동작을 수행하는 코드를
총 4단계에 거쳐서 기술하고자 한다.
그 세 번째는 JPA를 활용하는 방법이다.
기존 코드(Service, Repository, Domain 등)는 아래 링크에서
순차적으로 볼 수 있다
https://healthdevelop.tistory.com/entry/spring12
● JPA
JPA란,
자바 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용되는
인터페이스의 모음이다.
ORM은,
객체(Object)와 관계형 데이터베이스(Relational Db)의 테이블을 Mapping 한다는 뜻이다.
정리하자면,
JPA란 인터페이스로부터 구현된 기술들을 사용하여
객체와 테이블을 매핑한다.
대표적인 매핑 프레임워크로는 hibernate가 있다.
● JPA의 장점
- JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
- JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있다.
- JPA를 사용하면 개발 생산성을 크게 높일 수 있다.
이제 JPA를 활용하여
Spring에서 db와 연동하는 코드를 작성해보자.
1. build.gradle 세팅
build.gradle에 dependencies에서
밑줄 친 코드를 삽입해준다. (spring에서 jpa를 사용하는 dependency)
2. DB 접속 정보 작성
jpa 설정을 했다면 이제 jpa 설정이 필요하다.
application.properties에 가서
아래 코드를 삽입해준다.
# jpa가 생성한 sql을 볼 수 있음
spring.jpa.show-sql=true
# jpa가 자동으로 테이블 생성하는 것을 막음 (!= create)
spring.jpa.hibernate.ddl-auto=none
이미 테이블을 작성했다고 가정하여
테이블 자동생성을 막는 코드도 추가하였다.
이로써 jpa 설정은 모두 끝났다.
3. 객체와 db 매핑
위에서 설명했듯이, jpa는 ORM 기술 표준으로 사용되고 있다.
jpa를 적용하기 위해선
작성했던 객체(domain) 클래스에 db를 매핑해주는 코드를 추가하여야 한다.
추가된 코드는 아래와 같다.
Member.java
package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
// @Entitiy: jpa가 관리하는 entity
@Entity
public class Member {
// pk 매핑
@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;
}
}
가장 중요한 것은
@Entity 애노테이션이다.
이 애노테이션을 상단에 작성함으로써
spring은 Member 클래스를 jpa가 관리하는 entity로 설정해준다.
4. 코드(JPA) 작성
기존에 작성했던 memberRepository를 활용하여
회원 가입, 회원 조회, 전체 회원 조회 코드를 작성하고자 한다.
JpaMemberRepository.java
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) {
em.persist(member); // .persist를 사용함으로써 연동된 db에 매개변수로 온 member가 db에 저장이된다(jpa가 알아서 쿼리 실행)
return member;
}
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id); // em.find를 통해 조회할 타입과 식별자를 넣음으로써 조회를 해줌
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) // :name 값에 매개변수로 온 name으로 넣어줌
.getResultList();
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class) // Member 엔티티를 조회
.getResultList();
}
}
클래스를 살펴보면
EntityManager 라는 것이 있다.
EntityManager는
우리가 jpa를 사용하기 위해 build.gradle에 작성했던 코드를 삽입하면
spring에서 db와 통신을 담당하는 entitymanager를 생성해준다.
jpa를 사용하기 위해선 이 entitymanager를 사용해야 한다.
jpa를 사용하면 기본적인 저장, 조회 등은 따로 코드를 작성할 필요가 없다.
(em.persist, em.find를 활용하면 됨)
여기서 주의할 점이 있다.
jpa를 사용하면 service 계층에
@Transactional 애노테이션을 추가해야한다.
서비스 계층에 트랜잭션 추가
import org.springframework.transaction.annotation.Transactional
@Transactional
public class MemberService {}
org.springframework.transaction.annotation.Transactional 를 사용하자.
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고,
메서드가 정상 종료되면 트랜잭션을 커밋한다.
만약 런타임 예외가 발생하면 롤백한다.
JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
4. Config 작성
위에 작성한 코드들을 작성했던 memberRepository interface로 넘겨주어야 한다.
그것은 이전에 작성했던 config 파일을 수정해서 할 수 있다.
package hello.hellospring;
import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.JdbcTemplateMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
return new JpaMemberRepository(em); // 추가
}
memberRepository 빈의 return을
새로 작성한 JpaMemberRepository로 반환해주는 것이다.
그리고 매개변수로 em를 넘겨주었다.
스프링에 의해 생성된 entitymanger를 매개변수로 삼는 것이다.
이제 데이터 연동은 마쳤고,
실제로 회원 가입이 잘 되는지 확인해 보자.
● 서버(브라우저)에서 확인
서버를 실행시키고,,
회원 가입을 페이지에 접속을 요청하고
새로운 회원 "jpa"를 등록시켜준다음
회원 조회 페이지로 가보면,,
새로운 회원이 "jpa"가 등록되었다.
h2 데이터베이스도 확인을 해보면
데이터가 잘 저장되었다.
우리(개발자)는 기존의 MemoryMemberRepository를 수정하지 않고
SpringConfig만 수정함으로써 db 저장소를 바꾸었다.
이것은 객체 지향 설계 5원칙 중 하나인 OCP(개방-폐쇄 원칙)을 잘 준수한 것이다.
기존에 있는 상태에서 확장은 하였지만, 기존 코드는 전혀 손을 대지 않았다.
이것이 스프링의 장점이다.
'Java > Spring' 카테고리의 다른 글
[Spring] 스프링(Spring)이란? | 스프링의 장점,특징 | 스프링 프레임워크(Spring Framework) | 스프링 부트 (0) | 2022.01.13 |
---|---|
[Spring] 스프링 데이터 JPA | 스프링 데이터 JPA로 DB(데이터베이스) 연동하기 | 회원 가입, 회원 조회 (0) | 2022.01.10 |
[Spring] DB(데이터베이스) 연동하기 | Spring JDBC Template | 회원 가입, 회원 조회 (0) | 2022.01.10 |
[Spring] 스프링 통합 테스트 | DB(데이터베이스) 연동하기 | 회원 가입, 회원 조회, 중복 회원 예외 (0) | 2022.01.10 |
[Spring] DB(데이터베이스) 연동하기 | 순수 JDBC | 회원 가입, 회원 조회 (0) | 2022.01.10 |