목차
- H2 데이터베이스 설치
- 데이터 베이스 접근 기술 (차례대로 4가지)
- 순수 Jdbc : (20년 전 쯤..)
- 스프링 JdbcTemplate : 스프링에서 Jdbc를 편리하게 쓸 수 있도록 제공
- JPA : 개발자가 직접 쿼리를 짜지 않아도 데이터베이스를 조작할 수 있게됨 (10년전쯤..)
- 스프링 데이터 JPA : 스프링에서 JPA를 편리하게 쓸 수 있도록 개발된 기술
JPA
사실 JPA는 스프링만큼 큰 기술이라, 따로 강의를 보고 책도 보고 ... 여튼 매우 깊게 공부해야할 분야라고 한다.
JPA
- 반복코드를 줄여줌
- SQL문을 직접 작성해줌
- SQL과 데이터 중심의 설계 → 객체 중심의 설계로 패러다임 전환 가능
- 개발 생산성의 증가
JPA는 (자바표준)인터페이스고 구현체로 (특정 업체들이 개발한 것)hibernate를 사용
O(bject)R(elational)M(apping) 을 이용한 기술이다.
Object(객체)와 Relational(관계형)을 mapping한다는 의미로, Mapping에는 Entity
환경설정
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'
// implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
}
이 설정을 해두면, 스프링 부트가 자동으로 EntityManger*를 생성해서 현재 데이터베이스와 연결해준다.
application.properties 수정
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
#jpa가 날리는 sql을 볼 수 있음
spring.jpa.show-sql=true
#jpa는 객체를 보고 table도 자동생성함, 하지만 지금은 만들어진 테이블을 이용할 것이기 때문에 꺼두기
spring.jpa.hibernate.ddl-auto=none
Domain과 DB 맵핑
이제 도메인으로 만들어둔 멤버와 데이터베이스 내의 테이블을 맵핑해준다.
package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //DB에서 자동으로 값을 넣어주는 것일때
private Long id; // serial number
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라는 annotation을 이용한다.
JPA 레포지토리 생성
JpaMemberRepository
이제 JPA를 이용하여 레포지토리를 구성하기 위해 클래스를 생성한다.
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{
//jpa는 EntityManger를 이용하여 모든것이 동작된다.
private final EntityManager em;
public JpaMemberRepository(EntityManager em){
this.em = em;
}
@Override
public Member save(Member member) {
em.persist(member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
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).getResultList();
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class).getResultList();
}
}
EntityManager 생성
//jpa는 EntityManger를 이용하여 모든것이 동작된다.
private final EntityManager em;
public JpaMemberRepository(EntityManager em){
this.em = em;
}
JPA는 EntityManger를 이용해 모든것이 작동된다.
따라서, EntityManger를 선언해야하는데, EntityManager는 스프링 부트가 자동으로 환경설정에 작성한 내용을 반영해서 생성한다. (사용하는 데이터베이스와 연결하거나, sql열람을 true로 해둔것 등등)
회원가입
@Override
public Member save(Member member) {
em.persist(member);
return member;
}
JPA에서는 저장,조회, 업데이트, 삭제는 sql짤필요가 없다.
조회(pk이용)
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id); //find(조회 타입, 식별자(pk))
return Optional.ofNullable(member);
}
pk기반의 조회의 경우도 따로 SQL을 짤 필요없이 다음과 같이 작성할 수 있다.
조회(pk 이용이 아닌 경우)
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class).getResultList();
}
pk기반이 아닌경우, pk로 조회하는 경우가 아닐시에는 JPQL이라는 것을 이용해야한다.
이는 단순 select가 아닌 List를 이용해 반환해주어야 하므로 find를 사용하지 않고 JPQL을 이용한다.
SQL문
SELECT (select대상 attribute등) FROM (TABLE)
JPQL문
SELECT (엔티티 객체 자체) FROM (엔티티)
위와 같은 차이가 있다.
JPQL문은 객체(Entity)를 대상으로 테이블이 아닌 엔티티를 조회하도록 쿼리를 날림 (Entity를 조회하도록)
셀렉트의 대상도 마찬가지이다. SQL문에서는 셀렉트의 대상 * , id, name등등 원하는 attribute를 select했지만,
SQL문은 맴버 엔티티 객체 자체를 셀렉트한다.(이미 mapping이 되어있기 때문)
Name처럼 where를 사용하는 경우 setParameter를 이용하여 값을 넣어준다.
@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).getResultList();
return result.stream().findAny();
}
주의할 점은 데이터 베이스를 조회하거나 할 때는 무조건 Transaction을 신경써주어야 한다.
따라서 다음과 같이 MemberService에 추가해준다.
service>MemberSerivce
@Transactional
public class MemberService {
//생략
}
테스트해보기
테스트를 이용해 잘 짜엿는지 알아보자. 회원가입 테스트 코드를 돌려보면,
(저번 게시글에 작성했던 테스트 코드 사용하면됨)
어떤 SQL문이 사용되었는지 볼 수 있다.
스프링 데이터 JPA(프레임워크)
JPQL을 짜지 않을 수 있는 방법이 있다! 바로 스프링 데이터 JPA를 이용하는 것이다.
리포지토리 구현 클래스 없이 인터페이스만으로도 개발을 완료할 수 있을 정도,
CRUD기능도 제공하기 때문에
단순반복 코드를 줄어들게 하여 개발자가 핵심 비즈니스 로직을 개발하는데 집중할 수 있게 된다.
(이제 스프링데이터 JPA는 선택이 아닌 필수! 당연히 JPA에 대한 충분한 학습선행이 필수다 )
SpringDataJpaMemberRepository
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 { //자료형 <class T,ID 식별자pk자료형>
Optional<Member> findByName(String name);
}
여러가지 특성이 있는데,
1. 인터페이스로 작성된다.
특이한 점은 인터페이스로 작성해준다. MemberRepsitory와 JpaRepository를 extends해주어야 하며,
JpaRepository는 class의 자료형과, 식별자의 자료형을 적어넣는다.
이렇게 되면 스프링 jpa가 구현체를 자동으로 만들어준다. (지금까지는 jpa, jdbc모두 구현체를 직접 구현함) 또한, bean으로 등록도 자동으로 등록된다.
그렇기 때문에 구현체 클래스가 아닌 인터페이스로 제작한 것!
2. 공통적인 사항에 대해서는 이미 구현이 되어있다.
또한, findAll , save, findById는 구현이 필요없는데, 이는 jpaRepository안에 매우 기본적/공통적인 CRUD등이 전부 구현되어 있기 때문이다.
하지만, findByName처럼 specific한 경우에 대해서는 구현되어 있기 어렵다. (모든 시스템이 다르기 때문에)
이에 대해서는 개발자가 직접 구현해야한다.
3.JPQL을 자동으로 제공한다.
findByName을 보면, 내부 로직을 구현하지 않고 단순 메소드명과 매개변수만 작성해두었다.
findBy__And___(Type field명, Type field명)의 규칙만 지킨다면, spring jpa가 알아서 jpql문으로 번역해준다.
인터페이스 이름만으로 구현이 가능하다! (물론 복잡한 것은 추가적인 코드가 필요함)
예)
//JPQL select m from Member m where m.name = ?
Optional<Member> findByName(String name);
//JPQL select m from Member m where m.name and m.Id = ?
Optional<Member> findByNameAndId(String name, Long Id);
그림으로 정리하면 다음과 같다.
SpringConfig
이제 Config를 수정해준다.
package hello.hellospring;
import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
//spring api
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository) { //스프링 컨테이너에서 멤버리포지토리를 찾음(spring jpa가 자동으로 생성한)
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);//의존성 등록
}
}
지금까지와는 달리 MemberRepository라는 빈을 등록할 필요가 없다.
위에서 언급했듯 스프링 jpa가 구현체를 자동으로 생성하여 bean으로 자동 등록해주었기 때문에 그냥 호출해서 사용하면된다.
이제 테스트 코드를 돌려보면 오류없이 작동이 가능하다.
팁>
참고: 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고, 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용하면 된다. Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성할 수 있고, 동적 쿼리도 편리하게 작성할 수 있다. 이 조합으로 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브 쿼리를 사용하거나, 앞서 학습한 스프링 JdbcTemplate를 사용하면 된다.
'Backend > Spring' 카테고리의 다른 글
[Spring] 스프링에 대하여 (0) | 2021.09.18 |
---|---|
[Spring] AOP (0) | 2021.09.14 |
[Spring] 데이터베이스 접근 기술(Jdbc) (0) | 2021.09.14 |
[Spring] DB설치(h2 데이터베이스) (0) | 2021.09.14 |
[Spring] 웹 MVC개발 (0) | 2021.09.13 |