inblog logo
|
LifeLog, DevLog
    JPA

    JPA 다뤄보기

    KYJTHEYJ's avatar
    KYJTHEYJ
    Dec 26, 2025
    JPA 다뤄보기
    Contents
    JPA설정부

    JPA

    Java 와 RDBMS 간은 패러다임의 차이가 있다

    Java 는 객체로 데이터 관리를 할 수 있지만
    RDMBS 는 테이블과 컬럼으로 데이터를 관리하기 때문이다

    이 둘 간의 패러다임 차이를 해결하기 위한 ORM 중 하나인 JPA를 사용해보자

    설정부

    기본적인 그래들 선언은 생략한다

    mySql을 기준으로 서술

    application.properties 설정

    # ========== 데이터베이스 연결 (필수) ==========
    spring.datasource.url=jdbc:mysql://localhost:3306/mydb
    spring.datasource.username=root
    spring.datasource.password=1234
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    # ========== JPA 기본 설정 (필수) ==========
    # DDL 자동 생성 전략
    # - none: 아무것도 하지 않음 (운영 환경)
    # - create: 기존 테이블 삭제 후 재생성 (개발 초기에 사용)
    # - create-drop: create + 종료 시 테이블 삭제 (테스트 환경)
    # - update: 변경된 스키마만 반영 (운영 비추천) (개발 중)
    # - validate: 엔티티와 테이블 일치 여부만 검증 (운영 환경)
    spring.jpa.hibernate.ddl-auto=update
    
    # SQL 로그 출력
    spring.jpa.show-sql=true
    
    # SQL 포맷팅
    spring.jpa.properties.hibernate.format_sql=true
    
    # 데이터베이스 방언 (자동 감지되지만 명시 권장)
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

    주요 환경별 권장 설정

    # ========== application-dev.properties (개발) ==========
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.show-sql=true
    spring.jpa.properties.hibernate.format_sql=true
    
    # ========== application-test.properties (테스트) ==========
    spring.jpa.hibernate.ddl-auto=create-drop
    spring.jpa.show-sql=false
    
    # ========== application-prod.properties (운영) ==========
    spring.jpa.hibernate.ddl-auto=validate
    spring.jpa.show-sql=false
    spring.jpa.properties.hibernate.format_sql=false

    JPA 포함 사용시 프로젝트 구조

    이런 예시대로 구조를 만들어 나가는게 좋아보인다

    Entity 선언

    package kyjtheyj.memo_demo.entity;
    
    import jakarta.persistence.*;
    import lombok.AccessLevel;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    @Entity
    @Getter
    @Table(name = "memos")
    // 기본 생성자 접근제한
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Memo {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String contents;
    
        public Memo(String contents) {
            this.contents = contents;
        }
    
    	// 업데이트용
        public void update(String contents) {
            this.contents = contents;
        }
    }
    

    Controller, Service, Repository, DTO

    @RestController
    @RequiredArgsConstructor
    public class MemoController {
        private final MemoService service;
    
        @PostMapping("/memos")
        public ResponseEntity<CreateMemoResponse> createMemo(@RequestBody CreateMemoRequest request) {
            return ResponseEntity.status(HttpStatus.CREATED).body(service.createMemo(request));
        }
    
        @GetMapping("/memos/{id}")
        public ResponseEntity<GetOneMemoResponse> getOneMemo(@PathVariable Long id) {
            return ResponseEntity.status(HttpStatus.OK).body(service.getOneMemo(id));
        }
    
        @GetMapping("/memos")
        public ResponseEntity<List<GetOneMemoResponse>> getAllMemo() {
            return ResponseEntity.status(HttpStatus.OK).body(service.getAllMemo());
        }
    
        @PatchMapping("/memos/{id}")
        public ResponseEntity<UpdateMemoResponse> updateMemo(@PathVariable Long id, @RequestBody UpdateMemoRequest request) {
            return ResponseEntity.status(HttpStatus.OK).body(service.updateMemo(id, request));
        }
    
        @DeleteMapping("/memos/{id}")
        public ResponseEntity<Void> updateMemo(@PathVariable Long id) {
            service.deleteMemo(id);
            return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
        }
    }
    @Service
    @RequiredArgsConstructor
    public class MemoService {
    
        public final MemoRepository repository;
    
        @Transactional
        public CreateMemoResponse createMemo(CreateMemoRequest request) {
            Memo memo = new Memo(request.content());
            Memo savedMemo = repository.save(memo);
            return new CreateMemoResponse(savedMemo.getId(), savedMemo.getContents());
        }
    
        @Transactional(readOnly = true)
        public GetOneMemoResponse getOneMemo(Long id) {
            Memo memo = repository.findById(id).orElseThrow(
                    () -> new IllegalStateException("발견된 메모 없음!")
            );
    
            return new GetOneMemoResponse(memo.getId(), memo.getContents());
        }
    
        @Transactional(readOnly = true)
        public List<GetOneMemoResponse> getAllMemo() {
            List<Memo> allMemo = repository.findAll();
            List<GetOneMemoResponse> responses = new ArrayList<>();
            for (Memo memo : allMemo) {
                responses.add(new GetOneMemoResponse(memo.getId(), memo.getContents()));
            }
            return responses;
        }
    
        @Transactional
        public UpdateMemoResponse updateMemo(Long id, UpdateMemoRequest request) {
            // 영속성
            Memo memo = repository.findById(id).orElseThrow(
                    () -> new IllegalStateException("발견된 메모 없음!")
            );
    
            memo.update(request.content());
            return new UpdateMemoResponse(memo.getId(), memo.getContents());
        }
    
        @Transactional
        public void deleteMemo(Long id) {
            Memo memo = repository.findById(id).orElseThrow(
                    () -> new IllegalStateException("발견된 메모 없음!")
            );
    
            repository.delete(memo);
        }
    }
    
    public interface MemoRepository extends JpaRepository<Memo, Long> {
    }
    
    public record CreateMemoRequest(Long id, String content) {
    }
    
    public record CreateMemoResponse(Long id, String content) {
    }
    
    public record GetOneMemoResponse(Long id, String content) {
    }
    
    public record UpdateMemoRequest(Long id, String content) {
    }
    
    public record UpdateMemoResponse(Long id, String content) {
    }
    • Update 할 때, 가져온 한개 조회 값의 memo 에 필드를
      Setter 메서드인 update를 갖고 필드의 값을 변경했는데도 업데이트가 된다

    • 이러한 점이 JPA 의 영속성인데 이에 대한 점은 추후 자세히 포스트 하려한다

    Share article
    Contents
    JPA설정부

    LifeLog, DevLog - https://github.com/KYJTHEYJ

    RSS·Powered by Inblog