본문 바로가기
Spring Boot/스프링 부트와 AWS로 혼자 구현하는 웹 서비스

Spring Boot JPA, Hibernate, Spring Data Jpa

by hyhs 2022. 2. 25.
728x90
반응형
SMALL

3장 

객체를 관계형 데이터베이스에서 관리하는 것 중요

패러다임 불일치 문제

-관계형 데이터베이스: 어떻게 데이터를 저장할지에 초점이 맞춰진 기술

-객체지향 프로그래밍 언어: 메시지를 기반으로 기능과 속성을 한 곳에서 관리하는 기술

 

JPA

서로 지향하는 바가 다른 2개 영역을 중간에서 패러다임 일치를 시켜주기 위한 기술

개발자는 객체지향적으로 프로그래밍을 하고, JPA가 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성해서 실행.

개발자는 항상 객체지향적으로 코드를 표현할 수 있으니 더는 SQL에 종속적인 개발을 하지 않아도 됨.

 

-JPA ← Hibernate ← Spring Data JPA

Spring Data JPA 

-구현체 교체의 용이성

Hibernate 외에 다른 구현체로 쉽게 교체하기 위함

-저장소 교체의 용이성

관계형 데이터베이스 외의 다른 저장소로 쉽게 교체하기 위함

 

spring-boot-starter-data-jpa

스프링 부트용 Spring Data Jpa 추상화 라이브러리

h2

인메모리 관계형 데이터베이스

별도의 설치없이 프로젝트 의존성만으로 관리

메모리에서 실행되기 때문에 애플리케이션을 재시작할 대마다 초기화된다는 점을 이용하여 테스트 용도로 많이 사용

 

-어노테이션 순서를 주요 어노테이션을 클래스에 가깝게

-Entity의 PK는 Long 타입의 Auto_increment 추천 (MySQL 기준 bigint 타입)

주민등록번호와 같이 비즈니스상 유니크 키나, 여러 키를 조합한 복합키로 PK를 잡을 경우 난감한 상황이 발생

 

-Entity 클래스에서는 절대 Setter 메소드를 만들지 않음

대신, 해당 필드의 값 변경이 필요하면 명확히 그 목적과 의도를 나타낼 수 있는 메소드를 추가해야함.

Setter가 없는 이 상황에서 DB에 어떻게 값을 채워 삽입?

생성자를 통해 최종값을 채운 후 DB에 삽입, 값 변경이 필요한 경우 해당 이벤트에 맞는 public 메소드 호출하여 변경

책에서는 생성자 대신 @Builedr를 통해 제공되는 빌더 클래스 사용 

 

생성자는 지금 채워야 할 필드가 무엇인지 명확히 지정할 수 없지만 빌더는 어느 필드에 어떤 값을 채워야 할지 명확하게 인지

Example.builder()
	.a(a)
    .b(b)
    .build();

 

ibas나 MyBatis 등에서 Dao라고 불리는 DB Layer 접근자

JPA에선 Repository라고 부름. JpaRepository<Entity 클래스, PK 타입>

-Entity 클래스와 기본 Entity Repository는 함께 위치해야함. 도메인 패키지에서 함께 관리

 

실행된 쿼리를 로그로 볼 수 있음.

application.properties 파일에서 spring.jpa.show_sql=true 

 

출력되는 쿼리 로그를 MySQL 버전으로 변경하는데 오류, 밑에 링크로 해결

https://github.com/jojoldu/freelec-springboot2-webservice/issues/612

->id bigint not null auto_increment로 나옴

 

-Service에서 비즈니스 로직을 처리하는게 아니라 Service는 트랜잭션, 도메인 간 순서 보장의 역할만 함.

 

Spring 웹 계층

 

Web Layer

-컨트롤러와 JSP/Freemarker 등의 뷰 템플릿 영역

-이외에도 필터, 인터셉터, 컨트롤러 어디브아시 등 외부 요청과 응답에 대한 전반적인 영역 이야기함.

 

Service Layer

-@Service에 사용되는 서비스 영역

-일반적으로 Controller와 Dao의 중간 영역에서 사용됨

-@Transactional이 사용되어야 하는 영역

 

Repository Layer

-DB와 같이 데이터 저장소에 접근하는 영역

-Dao 영역

 

Dtos

-Dto(Data Transfer Object)는 계층 간에 데이터 교환을 위한 객체를 이야기하며 Dtos는 이들의 영역을 얘기함.

-예를 들어 뷰 템플릿 엔진에서 사용될 객체나 Repository Layer에서 결과로 넘겨준 객체 등이 이들을 이야기함.

 

Domain Model

-도메인이라 불리는 개발 대상을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화 시킨 것

-택시 앱의 경우 배차, 탑승, 요구 등이 모두 도메인이 될 수 있음.

-@Entity가 사용된 영역 또한 도메인 모델

-무조건 DB의 테이블과 관계가 있어야만 하는 것은 아님. VO처럼 값 객체들도 이 영역에 해당

 

따라서 비즈니스 처리를 담당해야 하는 곳은 Domain임

기존에 서비스로 처리하던 방식을 트랜잭션 스크립트라고 함.

-모든 로직이 서비스 클래스 내부에서 처리, 서비스 계층이 무의미, 객체란 단순히 데이터 덩어리 역할만 함.

 

주문 취소 로직 슈도 코드(의사 코드)

 

@Transactional
public Order cancelOrder (int orderId) {
	1) 데이터베이스로부터 주문정보(Orders), 결제정보(Building), 배송정보(Delivery) 조회
    	2) 배송 취소를 해야하는지 확인
    	3) if(배송 중이라면) {
    		배송 취소로 변경
       }
    	4) 각 테이블에 취소 상태 Update
 }
@Transactional
public Order cancelOrder(int orderId) {

	OrderDto order = orderDao.selectOrders(orderId);
    BillingDto billing = billingDao.selectBilling(orderId);
    DeliveryDto delivery - deliveryDao.selectDelivery(orderId);
    
    String deliveryStatus = delivery.getStatus();
    
    if("IN_PROGRESS".equals(delivery.getStatus();
    	delivery.setStatus("CANCEL");
        deliveryDao.update(delivery);
    }
    
    order.setStatus("CANCEL");
    orderDao.update(order);
    
    billing.setStatus("CANCEL");
    deliveryDao.update(billing);
    
    return order;
    
}

반면 도메인 모델에서 처리할 경우 order, billing, delivery가 각자 본인의 취소 이벤트 처리, 서비스 메소드는 트랜잭션과 도메인 간의 순서만 보장 

@Transactional
public Order cancelOrder(int orderId) {

	
    Orders order = orderRepository.findById(orderId);
    Billing billing = billingRepository.findByOrderId(orderId);
    Delivery delivery = deliveryRepository.findByOrderId(orderId);
    
    delivery.cancel();
    
    order.cancel();
    billing.cancel();
    
    return order;
    
}

 

스프링에서 Bean을 주입받는 방식

-@Autowired

-setter

-생성자(권장)

 

Entity 클래스가 Dto 클래스와 거의 유사한 형태임에도 추가로 생성함.

-Entity 클래스를 Request/Response 클래스로 사용해서는 안됨

-Entity 클래스는 데이터베이스와 맞닿은 핵심 클래스. Entity 클래스를 기준으로 테이블이 생성되고 스키마가 변경됨. 

화면 변경은 아주 사소한 기능 변경이나 이를 위해 테이블과 연결된 Entity 클래스를 변경하는 것은 너무 큰 변경임.

수많은 서비스 클래스나 비즈니스 로직들이 Entity 클래스를 기준으로 동작하는데 Entity 클래스가 변경되면 여러 클래스에 영향을 끼치지만, Request와 Response용 Dto는 View를 위한 클래스라 정말 자주 변경이 필요함.

 

View Layer과 DB Layer의 역할 분리를 철저하게 하는게 좋음. Controller에서 결괏값으로 여러 테이블을 조인해줘야 할 경우가 빈번 - Entity 클래스만으로 표현하기 어려움.

-Entity 클래스와 Controller에서 쓸 Dto는 분리해서 사용해야 함.

 


3장 - 2, 110p

 

@WebMvcTest의 경우 JPA 기능이 작동하지 않기 때문에 Controller와 ControllerAdvice 등 외부 연동과 관련된 부분만 활성화되니 JPA 기능까지 한번에 테스트할 때는 @SpringBootTest와 TestRestTemplate 사용

 

 

위 오류로 시간을 허비했는데 PostSaveRequestDto에 @Getter, @NoArgsConstructor 어노테이션을 빼먹은 거였다ㅠ

 

update 기능에서 데이터베이스에 쿼리를 날리는 부분이 없음. -JPA의 영속성 컨텍스트 때문에 가능

-영속성 컨텍스트란 엔티티를 영구 저장하는 환경

-JPA의 핵심 내용은 엔티티가 영속성 컨텍스트에 포함되어 있냐 아니냐로 갈림.

-JPA의 엔티티 매니저가 활성화된 상태로 트랜잭션 안에서 데이터베이스에서 데이터를 가져오면 이 데이터는 영속성 컨텍스트가 유지된 상태임

-더티 체킹

이 상태에서 해당 데이터의 값을 변경하면 트랜잭션이 끝나는 시점에 해당 테이블에 변경분을 반영

즉, Entity 객체의 값만 변경하면 별도로 Update 쿼리를 날릴 필요가 없다.

 

JPA Auditing으로 생성시간/수정시간 자동화

LocalDate 

 

 

3장에서 배운 것

-JPA/Hibernate/Spring Data Jpa의 관계

-Spring Date Jpa를 이용하여 관계형 데이터베이스를 객체지향적으로 관리하는 방법

-JPA의 더티 체킹을 이용하면 Update 쿼리 없이 테이블 수정이 가능하다는 것

-JPA Auditing을 이용하여 등록/수정 시간을 자동화하는 방법

728x90
반응형
LIST

댓글