[Spring]메모장 프로젝트-JPA

김세림·2024년 5월 22일

Spring_코드공부

목록 보기
3/3
post-thumbnail

메모장 프로젝트-JPA


서론

이전의 데이터베이스 연결을 JDBC로만 했었는데 이를 JPA로 바꿀 예정이다.
또한, 작성일자와 수정일자를 Auditing을 적용하여 표현해보겠다.

세팅

application.properties

spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

해당 hibernate를 추가한다.
아래 3가지는 DB에 요청하는 모든 SQL을 보기좋게 출력해주기 위함이며
ddl-auto는 create, create-drop, update, validate, none 을 제공해준다.

build.gradle

 // JPA 설정
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

해당 의존성을 추가한다.

Memo

entity를 jpa에서 사용할 수 있게끔 만들어주어야한다.

package com.sparta.memo.entity;

import com.sparta.memo.dto.MemoRequestDto;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Getter
@Setter //조심히 사용!!..
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
@NoArgsConstructor
public class Memo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "username", nullable = false)
    private String username;
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;

    public Memo(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }

    public void update(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
}

이렇게 @Entity 애너테이션으로 클래스를 지정하고 @Table 애너테이션으로 직접 DB의 memo 이름의 테이블과 매핑하여 사용하는 방식으로 진행하였다.

Spring Data JPA

MemoRepository.java

해당 repository를 JpaRepository를 상속받는 인터페이스로 우선 만들어 버릴것이다!

public interface MemoRepository extends JpaRepository<Memo, Long> {

}

MemoService.java

package com.sparta.memo.service;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;

import java.util.List;
//비즈니스 로직
@Service
public class MemoService {

    private final MemoRepository memoRepository;

    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
    public MemoResponseDto createMemo(MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);

        // DB 저장
        Memo saveMemo = memoRepository.save(memo);

        // Entity -> ResponseDto
        MemoResponseDto memoResponseDto = new MemoResponseDto(memo);

        return memoResponseDto;
    }

    public List<MemoResponseDto> getMomos() {
        return memoRepository.findAll().stream().map(MemoResponseDto::new).toList();
    }

    @Transactional
    public Long updateMemo(Long id, MemoRequestDto requestDto) {
        //해당 메모 있는지 확인
        Memo memo = findMemo(id);
        // memo 내용 수정
        memo.update(requestDto);

        return id;

    }

    public Long deleteMemo(Long id) {
        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = findMemo(id);
        memoRepository.delete(memo);

        return id;
    }

    private Memo findMemo(Long id){
        // 해당 메모가 DB에 존재하는지 확인
        return memoRepository.findById(id).orElseThrow(()->
                new IllegalArgumentException("선택한 메모는 존재하지 않습니다.")
        );
    }

}

별로 달라지는 것은 없다!
왜냐 이전에 이미 repository에서 실행하는 메소드들의 이름을 jparepository에 있는 네임과 같이 코드를 짰었기 때문이다..
만약 다르게했다면 이것도 하나하나 찾아가면서 바꾸긴해야했을 것 같다.

여기에서 중요하다고 생각되는 부분은 updateMemo 메서드 위 Transactional 애너테이션이다.

먼저 JpaRepository를 보게되면

이러한 식으로 transactional 이 기본 클래스에는 readonly로만 되어있고, delete와 같이 삭제되는 부분이는 메소드 위에 readonly를 풀어놓은것을 확인할 수 있다.

그런데 이곳에 update와 같은 메소드는 없다고 jpa 포스트에서 말했었다!
이를 memo entity에서 update 메소드를 통해 수정해야하는 것이기 때문에 따로 JpaRepository에서 설정되어있지않아 해당 메소드 위에 Transactional을 따로 지정해준것이다.

JPA Auditing

Timestamped.java

package com.sparta.memo.entity;

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

import java.time.LocalDateTime;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {

    @CreatedDate
    @Column(updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    //Java Date패키지나 calendar 같은 날짜랑 mapping할때 사용 // spring에서 date, time, timestamp
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column
    @Temporal(TemporalType.TIMESTAMP) 
    private LocalDateTime modifiedAt;
}

이 클래스를 추가하며, Memo entity에 해당 클래스를 상속하여 create와 modified 시간 컬럼을 추가한다.

또한, Response받았을 때도 두 컬럼이 필요하므로 LocalDateTime 타입으로 해당 컬럼들을 추가한다.

MemoApplication.java

해당 클래스에도 auditing 관련 세팅을 해주어야한다.

package com.sparta.memo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class MemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MemoApplication.class, args);
    }

}

위 코드에서 import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing 이 두줄이 바로 그것이다.
해당 설정을 해서 auditing 기능을 사용할 수 있게 된다.

Query Methods

지금 우리 프로젝트에서 메모를 추가하면 가장 아래로 내려가게된다.
이렇게하면 최근에한게 계속 아래로 내려가서 확인하기 어려우니 내림차순으로 바꾸려고한다.

repository 인터페이스에 쿼리 메소드를 추가하면 쉽게 해결할 수 있다.

쿼리메소드 형식에 맞추어서

List<Memo> findAllByOrderByModifiedAtDesc();
메소드를 추가하고, service에 findAll부분을 해당 메소드로 변경하면된다.

순서대로 메소드를 뜯어서 보자면
findAllBy : 모든것을 찾을건데
OrderBy : 순서대로
ModifiedAtDesc : 수정시간을 내림차순으로!
라는 뜻이된다.

0개의 댓글