5. 스프링 DB 접근 기술
5) JPA
JPA: 기존 반복 코드 + 기본 SQL 대신 자동으로 처리 해줌
- JPA 사용 이유
- JdbcTemplate 의 업그레이드 버전 ∴ 작성 코드 더 간단해짐 (직접 작성하던 SQL 상당 부분 대체)
- SQL, 데이터 중심 설계 → 객체 중심 설계로 전환 가능 (객체만 신경쓰기 가능)
- 개발 생산성 증대 (객체를 JPA에 넣으면, 중간에서 DB에 SQL날리고 Data 가져오는 것들을 JPA가 다 처리해줌)
1. 설정 추가/ 수정
1) build.gradle에 dependency 추가/수정
// 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'
}
2) application.properties 수정
spring.jpa.show-sql=true
//jpa와 관련된 sql을 볼 수 있음
spring.jpa.hibernate.ddl-auto=none
//jpa 사용시 객체(member)를 보고 테이블도 자동으로 생성함 - 자동 생성 기능 off해주기
//반대로 테이블 자동생성을 원한다면 none -> create
다 받아졌다면 외부 라이브러리에 2가지 라이브러리가 들어와야 한다.
♘ JPA가 그래서 뭔데?
1) 표준인터페이스 자체를 말하는 것. 따라서 인터페이스만 제공이 됨
⇒ 구현체가 추가적으로 필요함 (구현 기술은 업체별로 다양하다) - 주로 hibernate만 사용
- 따라서 hibernate는 jpa의 구현체이다.
2) 객체 + ORM 기술
- ORM : Object + Relational + Mapping [객체와 Relational Database의 테이블을 매핑한다]
- 어떻게 매핑하는데?? ⇒ Annotation 사용함
2. Entity Mapping 해주기
import javax.persistence.Entity;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username")
private String name;
}
- Member Class에 Annotation을 추가해 줌 ➤ Member == JPA가 관리하는 Entity가 됨
- @Id(=PK) @GeneratedValue(strategy = GenerationType.IDENTITY)
- PK : ID(=PK)는 DB에서 자동으로 생성해 줌
- 이렇게 DB에 값을 넣으면 자동으로 생성해주는 것을 IDENTITY strategy라고 함
- @Column(name = "username")
- DB에 있던 Column명을 username과 mapping 되게 함
위 @Id ... , @Column Annotation을 통해 DB와 매핑을 하는 것. ⇒ 요 정보들을 이용해 JPA가 SQL문을 만들어 날림
3. Repository 생성 (JpaMemberRepository)
public class JpaMemberRepository implements MemberRepository{
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
- EntityManager
- JPA는 EntityManager로 모든 동작을 수행함 (스프링 부트가 현재 DB와 연결해서 자동으로 생성해 줌)
- 그러므로 이미 만들어진 EntityManager를 Injection만 하면 됨! (= JPA를 사용하려면 EntityManager를 주입 받아야 한다)
1) save
@Override
public Member save(Member member) {
//사실 이게 핵심
em.persist(member);
//구색 맞추기 위해 return
return member;
}
- JPA가 Insert 쿼리 스스로 만들어서 DB에 넣고, member에 setId까지 해줌
2) findById
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
- em.find(A, B) : 조회할 타입(A), 식별자(B)를 요소로 넣으면, 해당 식별자가 있는지 조회 가능
- Optional로 반환해야 하므로 ofNullable 붙여줌
3) findAll
@Override
public List<Member> findAll() {
List<Member> result = em.createQuery("select m from Member m", Member.class)
.getResultList();
return result;
}
- PK 기반 외의 것들을 조회하기 위해선, jpql라는 객체지향쿼리를 사용해야 함
- "select m from Member (as) m" : 테이블이 아닌 엔티티(객체)를 대상으로 쿼리문을 날림
- select 하는 것도 member 객체 자체를 select 하는 것 >> 추가적 매핑 필요 없음!
4) findByName
@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();
}
- 마찬가지로 PK가 아닌 값을 조회하므로 객체지향쿼리를 사용
- 비교값을 ? 대신 :name을 사용
- setParameter로 식별자 셋팅해줌
추후에 등장할 "Spring Data JPA" (JPA + Spring 진화 최최최종.ver)를 사용하면, 위에 두 경우에서도 jpql 안 짜도 됨ㅎ
+) MemberService 수정
JPA를 사용하기 위해선 Service 계층에 @Transactional 추가 필수!!!
- JPA는 data 변경이 발생하는 경우(ex. join), Transactional 안에서 수행 돼야 함
- 해당 클래스의 method 실행 시 Transaction 시작 >>> method 정상 종료시에만 commit (아니믄 롤백함)
4. 테스트 하기
1) SpringConfig 수정 - DI 삽입
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
...
@Bean
public MemberRepository memberRepository() {
return new JpaMemberRepository(em);
}
2) 테스트 코드 돌려보기 - MemberServiceIntegrationTest
이제 테스트만 돌리면 되겠군! 하고 돌렸는데...
왜 때문에 오류 발생?... 쿼리 세팅을 잘못한 것 같다.
알고보니 문제는 JPA 엔티티를 매핑하는 과정에서 발생했다!
Column "Member_username" not found >> 테이블을 보니 column명으로 Name은 있지만 username은 없다.
Column Annotation을 추가해서 발생한 문제같다. 삭제했더니 아래와 같이 잘 통과한다.
(강의 복습하다 전말을 알게되었다. column명이 username일 경우에 대한 추가설명에서 어노테이션이 등장했던 것...^^ 정신 체리~~~) → Annotation만 잘 활용하면 코드는 그대로 가져가되 DB만 갈아끼울수도 있겠군!
- jpa 인터페이스에선 기본적으로 hibernate라는 구현체가 사용됨을 알 수 있다
- select 쿼리, insert 쿼리가 동작함을 알 수 있다.
- 이때 insert 쿼리에서 id는 default 값으로 넣음 (DB에서 id 자동 생성해서 넣음)
+) JPA 기초 강의도 추가적으로 꼭꼭!!! 듣자 (이걸로는 택도 없다 ㅡ.ㅡ)
JPA에서 제일 중요한 것 = 객체/테이블을 잘 설계해서, 정확하고 명확하게 매핑하는 것!
'Spring > Spring boot 입문' 카테고리의 다른 글
[스프링 부트] AOP - 스프링 입문 강의 / 인프런 (0) | 2022.09.03 |
---|---|
[스프링 부트] 스프링 DB 접근 기술 (6)스프링 데이터 JPA - 스프링 입문 강의 / 인프런 (0) | 2022.08.31 |
[스프링 부트] 스프링 DB 접근 기술(4)- 스프링 입문 강의 / 인프런 (0) | 2022.08.30 |
[스프링 부트] 스프링 DB 접근 기술(1)~(3)- 스프링 입문 강의 / 인프런 (1) | 2022.08.28 |
[스프링 입문 강의] H2 DB 접근 오류 - Table "MEMBER" not found (this database is empty) (0) | 2022.08.28 |