Spring

[SpringBoot] Spring Data Jpa와 관계형 데이터베이스

줘니(•̀ᴗ•́)و 2024. 5. 13. 15:37
728x90

 

 

 

--- 저를 위한 공부입니다

많은 부분이 생략되어 있습니다 ---

 

 

 

 

1. MyBatis와 JPA

 

1) MyBatis - SQL Mapper - 쿼리를 매핑

2) JPA - 자바 표준 ORM(Object Relational Mapping) - 객체를 매핑

 

 

 

 

 

2. 관계형 데이터베이스와 객체지향 프로그래밍

 

1) 관계형 데이터베이스 : 어떻게 데이터를 저장할지에 초점

2) 객체지향 프로그래밍 : 메시지를 기반으로 기능과 속성을 한 곳에서 관리

 

 

* 먼저 JPA가 어떻게 등장하게 되었나?

 

→ 관계형 데이터 베이스와 객체지향 프로그래밍 언어의 패러다임이 서로 다른데, 객체를 데이터베이스에 저장하려고 하니 여러 문제가 발생 = 패러다임 불일치

 상속, 1:N 등 다양한 객체 모델링을 데이터베이스로는 구현할 수 X

→ 웹 애플리케이션 개발이 점점 데이터베이스 모델링에만 집중하게 됨

→ JPA는 이런 문제점을 해결하기 위해 등장!

 

 

* JPA(Java Persistence API)

 

: 객체-관계 매핑(ORM)을 위한 API를 제공

쉽게 말하면 객체지향 프로그래밍 언어와 관계형 데이터베이스를 중간에서 패러다임 일치를 시켜주기 위한 기술

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

→ 개발자는 이제 객체지향적으로 코드를 표현할 수 있으니 더는 SQL에 종속적인 개발을 하지 않아도 됨! 자바 객체와 데이터베이스 테이블 간의 매핑을 간편하게 처리 가능!@

객체 중심 개발의 장점은?  생산성이 향상되고, 유지보수가 편리하다는 것

 

 

 

 

 

3. Entity 클래스

 

: 실제 DB와 매칭될 클래스. DAO 패키지와는 결이 다름

(보통 도메인을 담을 패키지를 하나 생성하고 그 패키지에 Entity클래스 추가함

여기서 도메인이란?   게시글, 댓글, 회원, 결제 등 소프트웨어에 대한 요구사항 or 문제 영역)

그간 xml에 쿼리를 담고, 클래스는 쿼리의 결과만 담던 일들이 모두 도메인 클래스라고 불리는 곳에서 해결됨

 

1) 이 Entity 클래스에서는 절대 setter 메소드를 만들지 않음!!!

→ 값 변경이 필요하면 명확히 그 목적과 의도를 나타낼 수 있는 메소드를 추가할 것

   - 그럼 setter가 없는데 어떻게 값을 채워서 DB에 삽입하지?

     ① 기본적인 구조는 생성자를 통해 최종값을 채운 후 DB에 삽입

     ② 값 변경이 필요한 경우, 해당 이벤트에 맞는 public 메소드를 호출하여 변경할 것

   ①의 경우에서 생성자를 대신해 @Builder를 통해 제공되는 빌더 클래스를 사용할 것

   생성자나 빌더나 생성 시점에 값을 채워주는 역할은 같음

   but, 생성자의 경우 지금 채워야 할 필드가 뭔지 명확히 지정할 수 X

   빌더를 사용하면 어느 필드에 어떤 값을 채울건지 명확하게 지정할 수 있음

 

2) 수많은 서비스 클래스나 비즈니스 로직들이 Entity 클래스를 기준으로 동작하기 때문에 Request, Response 클래스로 사용해선 X

→ Request, Response 용 DTO는 View를 위한 클래스라 자주 변경이 필요하고, Controller에서 결괏값으로 여러 테이블을 조인해서 줘야 할 경우가 많아 Entity 클래스와 분리해서 사용해야 함

 

 

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@Entity
public class Posts {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 웬만하면 PK는 Long 타입의 Auto_increment를 추천

    @Column(length = 500, nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    // 컬럼 선언 안 해도 해당 클래스의 모든 필드는 모두 컬럼이 됨
    // 기본값 이외에 추가로 변경이 필요한 옵션이 있을 때 사용
    // 문자열의 경우 VARCHAR(255)가 기본값
    private String author;

    @Builder
    public Posts(String title, String content, String author){
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public void update(String title, String content){
        this.title = title;
        this.content = content;
    }

}

 

 

 

 

 

4. Service 역할에 대해

 

: Service는 트랜잭션, 도메인 간 순서를 보장하는 역할만 함(비즈니스 로직을 처리해야 한다는 건 오해임!)

그럼 비즈니스 로직은 누가 처리해??? 

→ 일단 먼저 스프링 웹 계층을 봐보자

 

① Web Layer

: Controller와 JSP 등 뷰 템플릿 영역. 필터, 인터셉터 등 외부 요청과 응답에 대한 전반적인 영역

② Service Layer

: Controller와 DAO의 중간 영역. @Transactional이 사용되어야 하는 영역

③ Repository Layer

: 데이터베이스와 같이 데이터 저장소에 접근하는 영역(DAO 영역으로 이해하면 됨)

④ DTO(Data Transfer Object)

: 계층 간 데이터 교환을 위한 객체. 뷰 템플릿 엔진에서 사용될 객체나 Repository Layer에서 결과로 넘겨준 객체

⑤ Domain Model

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

예를 들면, 택시 앱에서 배차, 탑승, 요금 등이 모두 도메인이 될 수 있음.

@Entity가 사용된 영역 역시 도메인 모델에 해당함

무조건 데이터베이스의 테이블과 관계가 있어야만 하는 건 아님. VO처럼 값 객체들도 이 영역에 해당함

 

다시 돌아와서 비즈니스 로직은 누가 담당해?????

Domain 임!!!

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

모든 로직이 서비스 클래스 내부에서 처리되고, 그러다보니 서비스 계층이 무의미하며, 객체란 단순히 데이터 덩어리 역할만 하게 됨.(회사에서  egov 쓰는데 이렇게 했다)

하지만 도메인 모델에서 처리하면, 서비스 메소드는 트랜잭션과 도메인 간 순서만 보장하게 됨

 

 

 

 

 

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

 

- Autowired

- setter

- 생성자

⇒ 이 중에서 가장 권장하는 방식이 생성자로 주입받는 방식

@RequiredArgsConstructor 로 final이 선언된 모든 필드가 포함된 생성자를 생성(final이 없는 필드는 생성자에 포함되지 X)

생성자를 직접 안 쓰고 롬복 어노테이션을 사용하는 이유 : 해당 클래스의 의존성 관계가 변경될 때마다 생성자 코드를 계속 수정할 필요가 없음!

 

 

 

 

 

6. 더티 체킹

 

: 데이터베이스와 연결된 애플리케이션에서 주로 사용되는 개념으로, 데이터베이스에 대한 변경 사항을 추적하고 변경 사항을 저장하는 기술

Hibernate와 같은 ORM 프레임워크에서 자주 볼 수 있음

 

* 더티 체킹의 원리

① 변경 감지 : 객체의 상태가 변경되면 ORM 프레임워크는 이를 감지

→ 변경된 속성이나 연관된 엔티티 등이 확인됨

② 변경 추적 : 변경된 엔티티나 속성은 추적되어 관리 대상으로 표시됨

→ 이를 통해 ORM은 어떤 속성이 변경되었는지 추적할 수 있음

③ 변경 저장 : 트랜잭션이 커밋되거나 영속성 컨텍스트를 플러시할 때, 더티 체킹은 변경된 엔티티를 데이터베이스에 반영

→ 변경된 엔티티만을 식별하여 데이터베이스에 업데이트함

 

 

* 더티 체킹의 장점

- 효율적인 업데이트 : 변경된 엔티티만 데이터베이스에 반영 → 전체 엔티티를 업데이트하는 것보다 효율적

- 트랜잭션 내 일관성 유지 : 트랜잭션 내에서 변경된 엔티티를 추적하고 일관성을 유지할 수 있음

 

 

 

 

 

7. JPA Auditing

 

: 보통 엔티티에는 해당 데이터의 생성, 수정시간을 포함함(언제 등록되었고 수정되었는지는 유지보수에 있어 굉장히 중요)

but, 등록 or 수정 할 때마다 반복적인 코드가 들어감 → 이 문제를 해결하는 게 JPA

AuditingBaseTimeEntity 클래스를 새로 만들고, Entity 클래스에서 BaseTimeEntity를 상속받으면 반복적인 코드를 작성하지 않고 사용할 수 있음

(Application 클래스에 @EnableJpaAuditing을 추가하여 JPA Auditing을 활성화시켜야 함)

 

 

import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@Getter
@MappedSuperclass                                   
// JPA Entity 클래스들이 BaseTimeEntity를 상속할 경우 필드들도(createdDate, modifiedDate) 칼럼으로 인식하도록 함
@EntityListeners(AuditingEntityListener.class)  // BaseTimeEntity 클래스에 Auditing 기능을 포함시킴
public abstract class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createdDate;      // Entity가 생성되어 저장될 때 시간 자동 저장

    @LastModifiedDate
    private LocalDateTime modifiedDate;     // Entity의 값을 변경할 때 시간 자동 저장
}

 

 

 

 

 

'스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 책으로 공부했습니다. 책 짱조음

 

 

 

728x90