해당 강의는 Inflearn에 등록된 김영한님의 Springboot 강의입니다.
이번 장에는 스프링 부트와 H2 db를 연동하고, jpa로 동작하는 과정을 설명하겠다.
1. 테이블 생성하기
다음과 같은 위치에 sql문 작성(회원 테이블 생성)
drop table if exists member CASCADE;
create table member
(
id bigint generated by default as identity,
name varchar(255),
primary key (id)
);
2. 데이터 베이스 환경 설정 하기
build.gradle
에 주석 처리 부분 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//★★★자바가 DB와 연동하기 위해선 jdbc 드라이버가 필요하다.
//implementation 'org.springframework.boot:spring-boot-starter-jdbc'
//★★★data-jpa: ★★jpa 드라이버+jdbc드라이버 포함이다. 따라서 Jdbc는 주석처리가능
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//데이터베이스의 클라이언트
runtimeOnly 'com.h2database:h2'
}
<- 이 버튼 클릭
resources/application.properties
에 추가
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=create
위를 보면 Jdbc, jpa 동시에 설정을 했다.
show-sql
: JPA가 생성하는 SQL을 출력한다.
ddl-auto
: JPA는 테이블을 자동으로 생성하는 기능을 제공하는데 none
를 사용하면 해당 기능을 끈다.
create
: 엔티티 정보를 바탕으로 테이블도 직접 생성해준다.
H2데이터 베이스 설치
아래를 클릭하면 h2 db 설치 홈페이지로 넘어간다.
H2 Database Engine (redirect)
H2 Database Engine Welcome to H2, the free SQL database. The main feature of H2 are: It is free to use for everybody, source code is included Written in Java, but also available as native executable JDBC and (partial) ODBC API Embedded and client/server mo
www.h2database.com
아래의 이미지에서 밑줄 친 부분 클릭하고, 그 뒤부턴 차례대로 위에서 설명한 것을 따라가면 된다.
3. JPA의 이점 (=순수 JDBC를 사용하지 않는 이유)
- JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
- JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있다.
- JPA를 사용하면 개발 생산성을 크게 높일 수 있다.
4. JPA로 개발(회원 등록,조회)
JPA 엔티티 매핑
//1.회원 객체 (도메인)
//★JPA는 이 클래스의 객체를 데이터베이스 테이블과 매핑하여 관리
@Entity //★★★이 클래스가 JPA에 의해 관리되는 엔티티임을 나타내는 annotation.
public class Member {
//@Id: 기본키(pk), @GeneratedValue: 데이터베이스가 자동으로 이 필드의 값을 생성하도록 지정
//GenerationType.IDENTITY는 데이터베이스가 자동으로 고유 키를 생성하도록 하는 전략을 의미합니다.
@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;
}
}
코드 설명
JPA
는 이 클래스의 객체(엔티티)
를 데이터베이스 테이블
과 매핑
하여 관리한다.
@Entity
: 이 클래스(객체)가 JPA에 의해 관리되는 엔티티임을 나타내는 annotation.@Id
: 기본키(pk)라 명시.@GeneratedValue
: 데이터베이스가 자동으로 이 필드의 값을 생성하도록 지정.strategy = GenerationType.IDENTITY
: 데이터베이스가 자동으로 고유 키를 생성하도록 하는 전략을 의미한다.
이전의 MemoryMemberRepository
를 보면 키값을 넣을때마다 1개씩 증가하도록 직접 구현했는데 이젠, JPA의 두가지 annotation
으로 간단하게 도메인
에 기본키
를 지정 해줬다.
public class MemoryMemberRepository implements MemberRepository{
//회원 저장 키,벨류 매핑
private static Map<Long,Member> store =new HashMap<>();//로컬 메모리에 저장
//sequence: 0,1,2 순으로 키값 생성
private static long sequence=0L;
JPA 회원 리포지토리
public class JpaMemberRepository implements MemberRepository{
//★★★jpa는 entity 매니저로 모든게 동작한다.
private final EntityManager em;
//이 매니저를 주입받음.
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
//회원 등록하기
@Override
public Member save(Member member) {
em.persist(member); //이렇게하면 jpa가 inset 쿼리 다 만들어서 집어 넣는다.
return member;
}
//id 조회하기
@Override
public Optional<Member> findById(Long id) {
Member member= em.find(Member.class,id); //pk 조회는 코드가 제일 단순하다
return Optional.ofNullable(member);
}
//멤버 name 조회해주기
@Override
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member as m where m.name= :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
//전체 멤버 조회해주기
@Override
public List<Member> findAll() {
//jpql 쿼리:"select m from Member as m"
List<Member> result= em.createQuery("select m from Member m",Member.class)
.getResultList(); //리스트로 반환
return result;//리스트로 반환
}
}
위 코드를 보면 총 4가지 기능 을 넣었다.
- 회원 등록하기
- 회원 id로 조회하기
- 회원 이름으로 조회하기
- 회원 전체 조회하기
추후 EntityManager
가 무엇이고, 영속성 컨텍스트
가 무엇인지 JPA 활용편
에서 정리 하겠다.
우선, jpa를 통해 영속성 콘텍스트라는 공간에 객체를 넣고, 조회한다고만 인지하면 된다. 이때,
em.create("쿼리",수행할 객체 클래스)쿼리문을 통해 조회
를 한다.
- 서비스 계층에 트랜잭션 추가
@Transactional //★★★★jpa 사용시 서비스에는 Transactional이 필요하다.
public class MemberService {
코드 설명
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을 커밋한다. 만약 런타임 예외가 발생하면 롤백한다. JPA를 통한 모든 데이터 변경
은 트랜잭션
안에서 실행해야 한다.
JPA를 사용하도록 스프링 설정 변경
실행결과
H2 DB
에서 실행
스프링 부트 콘솔창
- create 모드 설정 으로 인해 회원 객체 존재시 drop
- 초기 ddl.sql 파일을 통해 Member 테이블 생성
- 회원에서 같은 이름이 있는지 이름 조회 후 중복 내역이 없으므로 테이블에 회원 객체를 행으로 추가
- 회원 목록 조회
3
,4
는 다음과 같이 진행하면서 확인 하였음.
5. 스프링 데이터 JPA
스프링 데이터 JPA 회원 리포지토리
public interface SpringDataJpaMemberRepository extends JpaRepository<Member,Long> {
Optional<Member> findByName(String name);
}
코드 설명
JpaRepository
를 상속하기만 하면 CRUD
(create,read,upate,delete)를 할 수 있게된다.
다만, findByName
, findByEmail
과 같이 도메인의 속성에 맞추기 까진 어렵다. 하지만 위 코드를 보자.
Optional<Member> findByName(String name);
다음과 같이 명시만 해주면 리포지토리 인터페이스
에 Member 객체의 속성중 name이 있는지 찾아보고 그속성을 조회하는 메서드로 만들어준다.
이는 findBy"X"
의 문장 구조로만 작성하면 된다.
스프링 설정파일
//스프링 설정 파일
@Configuration
public class SpringConfig {
/*Spring data Jpa 사용*/
private final SpringDataJpaMemberRepository springDataJpaMemberRepository;
public SpringConfig( SpringDataJpaMemberRepository springDataJpaMemberRepository) {
this.springDataJpaMemberRepository = springDataJpaMemberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(springDataJpaMemberRepository);
}
}
코드 설명
전과 달리 설정 파일에서 단순히 MemberService
만 스프링 빈
으로 등록하면 된다.
스프링 빈 등록 포스팅
에서 언급한 내용과 마찬가지로 스프링 빈 등록시
아래의 코드와 같이 MemberService클래스의 생성자에 @Autowired
라고 하면 스프링 컨테이너
에서 스프링 빈
을 자동으로 등록하고 스프링 컨테이너에서 스프링이 알아서 관리 해준다. 이번 강의에는 DB 설정 없이 시작해서 리포지토리를 제대로 설정을 하지 않고, 시작했기에 따로 설정파일
을 만들어서 스프링 빈에 등록했다.
@Autowired
public MemberService(MemberRepository memberRepository)
{
this.memberRepository=memberRepository;
}
(사실 다음과 같이 롬복
으로 @RequiredArgsConstructor
만 적으면 된다. 추후 JPA 활용편
에서 설명하겠다.)
@Service
@RequiredArgsConstructor
public class MemberService {
Spring data Jpa를 활용한 MemberService
@Transactional
public class MemberService {
private final SpringDataJpaMemberRepository springDataJpaMemberRepository;
public MemberService(SpringDataJpaMemberRepository springDataJpaMemberRepository) {
this.springDataJpaMemberRepository = springDataJpaMemberRepository;
}
//회원가입
//1.중복이름이 아니면 추가
public Long join(Member member)
{
//중복이름의 회원 허용x 함수 사용
validateDuplicateMember(member);//1.중복 검증
springDataJpaMemberRepository.save(member); //2.회원 정보 저장(join)
return member.getId(); //3.해당 회원정보 반환
}
//이름 조회
private void validateDuplicateMember(Member member) {
springDataJpaMemberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
//전체회원 조회
public List<Member> findMembers()
{
return springDataJpaMemberRepository.findAll(); //List로 반환
}
//한 멤버 조회
public <Optional >Member findOne(Long memberId)
{
return springDataJpaMemberRepository.findById(memberId).get();
}
}
Spring Data Jpa를 활용하여 MemberService
에서 비즈니스 로직
을 구현했다.
결과
쿼리 결과
H2 db에서 확인
6. 스프링 데이터 JPA 제공 기능
인터페이스
를 통한 기본적인CRUD
findByName()
,findByEmail()
처럼 메서드 이름 만으로 조회 기능 제공페이징 기능
및정렬
제공- 원하는 범위 만큼(ex: 5~10) 조회 가능한 페이징 기법과, 조회시 데이터 정렬 기능도 있다.
스프링 데이터 JPA 제공 클래스 구조
위 구조를 보면 우리는 JpaRepository<엔티티,엔티티의 Id 타입>
만 상속 받아서 쓰면 많은 메서드들을 사용 할 수 있다.
보다 더 많은 이점들이 있지만 추후 정리해서 업로드 하겠다.