[JPA] RESTful API를 만들어보자. (CRUD)

2023. 11. 20. 01:26· Java/Spring Boot
목차
  1. 1. 환경
  2. 2. DB 구조
  3. 3. CRUD 구현
  4. (1). Entity 생성
  5. (2). DTO 생성
  6. (3). DTO -> Entity 변환 클래스 생성
  7. (4). Repository 생성
  8. (4). 전체 조회
  9. (5). 단건 조회
  10. (6). 등록
  11. (7). 수정
  12. (8). 삭제
  13. 4. Swagger 확인
  14. 5. PostMan을 이용하여 테스트 

1. 환경

1. Sping Boot

2. Spring Data JPA

3. Gradle

4. postgreSQL

5. PostMan

6. Swagger

7. 인텔리제이 무료버전


2. DB 구조


3. CRUD 구현

(1). Entity 생성

@Entity
@Getter
@Setter
@ToString
@Table(name = "room_cd")
public class EclassRoomEntity {

    @Id
    @Column(name = "room_cd_id", length = 36)
    private String roomCdId;

    @Column(nullable = false, length = 100)
    private String roomNm; // 강의실명

    @Column(nullable = false)
    private int hdcnt; // 정원, headCount

    @Column(length = 1, columnDefinition = "varchar (1) default 'N'")
    private String useYn; // 사용여부

    /* 기본 생성자 추가 */
    public EclassRoomEntity() {
    }

    /* 초기화용 생성자 */
    public EclassRoomEntity(String roomCdId) {
        this.roomCdId = roomCdId;
    }

    /* @Builder = 생성자 자동 생성 */
    @Builder
    public EclassRoomEntity(String roomCdId, String roomNm, int hdcnt, String useYn) {
        this.roomCdId = roomCdId;
        this.roomNm = roomNm;
        this.hdcnt = hdcnt;
        this.useYn = useYn;
    }

}

(2). DTO 생성

@Data // @Data = @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor
public class EclassRoomDTO {

    private String roomCdId; // 강의실 코드
    private String roomNm;   // 강의실명
    private int hdcnt;        // 정원
    private String useYn;     // 사용여부

    // 기본 생성자
    public EclassRoomDTO() {
    }

    public EclassRoomDTO(String roomCdId, String roomNm, int hdcnt, String useYn) {
    }

    /* ex) DB에서 조회할 때 강의실 정보를 Entity 객체에 담아오고, DTO로 변환하여 클라이언트에게 전달 */
    public static EclassRoomDTO toDTO(EclassRoomEntity entity) { // Entity -> DTO 변환
        EclassRoomDTO dto = new EclassRoomDTO(); // DTO 객체 생성
        dto.setRoomCdId(entity.getRoomCdId());
        dto.setRoomNm(entity.getRoomNm());
        dto.setHdcnt(entity.getHdcnt());
        dto.setUseYn(entity.getUseYn());
        return dto;
    }
}

(3). DTO -> Entity 변환 클래스 생성

public class EclassRoomEntityBuilder {

    public static EclassRoomEntity buildFromDTO(EclassRoomDTO eclassRoomDTO) { // DTO -> Entity 변환
        return EclassRoomEntity.builder() // builder을 이용하여 entity 객체 생성
                .roomCdId(eclassRoomDTO.getRoomCdId())
                .roomNm(eclassRoomDTO.getRoomNm())
                .hdcnt(eclassRoomDTO.getHdcnt())
                .useYn(eclassRoomDTO.getUseYn())
                .build();
    }

    /* EclassRoom Insert roomCdId 자동 증가용 */
    public static EclassRoomEntity buildNewId(String newRoomCdId, EclassRoomDTO eclassRoomDTO) { // DTO -> Entity 변환
        return EclassRoomEntity.builder() // builder을 이용하여 entity 객체 생성
                .roomCdId(newRoomCdId)
                .roomNm(eclassRoomDTO.getRoomNm())
                .hdcnt(eclassRoomDTO.getHdcnt())
                .useYn(eclassRoomDTO.getUseYn())
                .build();
    }

}

(4). Repository 생성

 
				                                // Entity명 , PK Type
public interface EclassRoomRepository extends JpaRepository<EclassRoomEntity, String> {
 			
}

(4). 전체 조회

1). Controller

@RestController
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
@Tag(name = "Eclass - Room")
public class EclassRoomController {

	private final EclassRoomService eclassRoomService; // 생성자 주입 방식

	/* @RestController이 적용되면 @ResponseBody를 사용하지 않아도 자동으로 됨 /*
	/* Test Class 생성 단축키 : Ctrl + Shift + T */

	/* 전체 조회 */
	@GetMapping("/selectEclassRoomList")
	@Operation(summary = "강의실 리스트", description = "강의실 리스트")
	public ResponseEntity<List<EclassRoomDTO>> getAllEclassRooms() {
		
		// Service에서 모든 강의실 정보를 조회하여 List<EclassRoomEntity>로 받아옴
		List<EclassRoomEntity> eclassRoomEntityList = eclassRoomService.getAllEclassRooms();

		List<EclassRoomDTO> eclassRoomDTOList = eclassRoomEntityList
				.stream() // List -> stream()으로 변환
				.map(EclassRoomDTO::toDTO) // 각각의 EclassRoomEntity를 EclassRoomDTO로 변환
				.collect(Collectors.toList()); // stream() -> List 변환

		// 강의실 정보를 ResponseEntity로 감싸서 반환
		// 성공 시 200 코드와 정보가 담긴 eclassRoomDTOList가 담긴 ResponseEntity를 반환
		return new ResponseEntity<>(eclassRoomDTOList, HttpStatus.OK);
	}

}

2). Service

@Service
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
public class EclassRoomService {

    private final EclassRoomRepository eclassRoomRepository;

    /* Test Class 생성 단축키 : Ctrl + Shift + T */
    /* 전체 조회, 단건 조회 = Entity -> DTO 변환
    *  등록, 수정 = DTO -> Entity 변환
    *  클라이언트 -> DB인지, DB -> 클라이언트인지 생각하면 됨 */

    /* 전체 조회 */
    public List<EclassRoomEntity> getAllEclassRooms() {

        /* Repository에 find.All()을 사용하여 모든 강의실 정보를 DB에서 가져온다.
        *  조회된 강의실 정보를 리스트로 반환한다.*/
        return eclassRoomRepository.findAll(); // findAll() = 전체 조회
    }
}

(5). 단건 조회

1). Controller


@RestController
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
@Tag(name = "Eclass - Room")
public class EclassRoomController {

	private final EclassRoomService eclassRoomService; // 생성자 주입 방식

	/* @RestController이 적용되면 @ResponseBody를 사용하지 않아도 자동으로 됨 /*
	/* Test Class 생성 단축키 : Ctrl + Shift + T */

	/* 단건 조회 */
	@GetMapping("/selectEclassRoomList/{roomCdCi}")
	@Operation(summary = "강의실 단건 조회", description = "강의실 단건 조회")
	public ResponseEntity<EclassRoomDTO> getEclassRoom(@PathVariable String roomCdCi) {
		
		// Service에 getEclassRoom(roomCdCi)를 호출하여 단일 정보를 가져오고 DTO에 저장.
		// Service에서 Controller로 넘겨줄 때 Entity -> DTO로 변환하여 넘겨줌
		EclassRoomDTO eclassRoomDTO = eclassRoomService.getEclassRoom(roomCdCi);

		if (eclassRoomDTO == null) { // eclassRoomDTO가 null이면 해당 404 에러 코드 반환
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		} else if (eclassRoomDTO.getRoomCdId() == null) { // roomCdId가 null이면 해당 404 에러 코드 반환
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		} else {
			// 성공 시 200 코드와 정보가 담긴 eclassRoomDTO가 담긴 ResponseEntity를 반환
			return new ResponseEntity<>(eclassRoomDTO, HttpStatus.OK); 
		}
	}
}

2). Service

@Service
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
public class EclassRoomService {

    private final EclassRoomRepository eclassRoomRepository;

    /* Test Class 생성 단축키 : Ctrl + Shift + T */
    /* 전체 조회, 단건 조회 = Entity -> DTO 변환
    *  등록, 수정 = DTO -> Entity 변환
    *  클라이언트 -> DB인지, DB -> 클라이언트인지 생각하면 됨 */
    
    /* 단건 조회 */
    public EclassRoomDTO getEclassRoom(String roomCdCi) {

        // roomCdCi에 맞는 강의실 정보를 가져온다. 해당되는 강의실이 없으면 예외 발생
        EclassRoomEntity entity = eclassRoomRepository.findById(roomCdCi) // findById(roomCdCi) = roomCdCi를 이용하여 단건 조회
                .orElseThrow(() -> new EntityNotFoundException("roomCdCi not found ! : " + roomCdCi)); // roomCdCi가 없으면 예외

        return EclassRoomDTO.toDTO(entity); // entity를 DTO로 변환하여 반환
    }
}

(6). 등록

1). Controller

@RestController
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
@Tag(name = "Eclass - Room")
public class EclassRoomController {

	private final EclassRoomService eclassRoomService; // 생성자 주입 방식

	/* @RestController이 적용되면 @ResponseBody를 사용하지 않아도 자동으로 됨 /*
	/* Test Class 생성 단축키 : Ctrl + Shift + T */

	/* 등록 */
	@PostMapping("/InsertEclassRoom")
	@Operation(summary = "강의실 등록", description = "강의실 등록")
	public ResponseEntity<EclassRoomDTO> createEclassRoom(@RequestBody EclassRoomDTO eclassRoomDTO) {
		
		/* eclassRoomService.createEclassRoom(eclassRoomDTO)을 호출하여
		   등록 정보가 담긴 DTO를 사용하여 강의실을 등록하고, 등록된 정보를 createdRoom에 저장 */
		EclassRoomDTO createdRoom = eclassRoomService.createEclassRoom(eclassRoomDTO);

		// 강의실 정보를 ResponseEntity로 감싸서 반환
		return new ResponseEntity<>(createdRoom, HttpStatus.CREATED);
	}
}

2). Service

@Service
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
public class EclassRoomService {

    private final EclassRoomRepository eclassRoomRepository;

    /* Test Class 생성 단축키 : Ctrl + Shift + T */
    /* 전체 조회, 단건 조회 = Entity -> DTO 변환
    *  등록, 수정 = DTO -> Entity 변환
    *  클라이언트 -> DB인지, DB -> 클라이언트인지 생각하면 됨 */

    /* 등록 */
    public EclassRoomDTO createEclassRoom(EclassRoomDTO eclassRoomDTO) {
        // Repository에서 JPQL 가져오기
        Integer maxNumericValue = eclassRoomRepository.findMaxNumericValue();

        // 현재 roomCdCI이 null이면 초기값으로 0 설정
        if (maxNumericValue == null) {
            maxNumericValue = 0;
        }

        // 현재 최대값에서 1을 더하여 새로운 roomCdId 생성 | MA-1 -> MA-2 ...
        int newNumericValue = maxNumericValue + 1;
        String newRoomCdId = "MA-" + newNumericValue;

        // newRoomCdId, DTO를 roomEntity에 담고 저장
        EclassRoomEntity roomEntity = EclassRoomEntityBuilder.buildNewId(newRoomCdId, eclassRoomDTO); // 객체 생성

        // roomEntity를 DB에 저장하고, 저장된 Entity를 savedEntity에 반환
        EclassRoomEntity savedEntity = eclassRoomRepository.save(roomEntity); // save = 엔티티가 있으면 수정, 없으면 등록

        return EclassRoomDTO.toDTO(savedEntity); // Entity -> DTO 변환하여 반환
    }
}

3). Repository

public interface EclassRoomRepository extends JpaRepository<EclassRoomEntity, String> {

    // 등록 시 현재 DB에서 가장 큰 roomCdId SELECT하는 쿼리문
    /*
        room_cd_id에서 MA-다음 숫자 부분을 추출하고 최대값을 찾는다.
        room_cd_id에서 4번째 문자부터 끝까지 문자열을 추출하고 문자열 -> 정수로 변환
        nativeQuery = true == JPA 쿼리(JPQL)가 아닌 SQL 쿼리를 사용할 때는 적어줘야됨
        service에서도 쿼리를 작성할 수 있지만 가독성을 위해 Repository에 작성함
    */
    @Query(value = "SELECT MAX(CAST(SUBSTRING(room_cd_id, 4) AS int)) FROM room_cd", nativeQuery = true)
    Integer findMaxNumericValue();

}

(7). 수정

1). Controller

@RestController
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
@Tag(name = "Eclass - Room")
public class EclassRoomController {

	private final EclassRoomService eclassRoomService; // 생성자 주입 방식

	/* @RestController이 적용되면 @ResponseBody를 사용하지 않아도 자동으로 됨 /*
	/* Test Class 생성 단축키 : Ctrl + Shift + T */

	/* 수정 */
	@PutMapping("/UpdateEclassRoom/{roomCdCi}")
	@Operation(summary = "강의실 수정", description = "강의실 정보 수정")
	public ResponseEntity<EclassRoomDTO> updateEclassRoom(@PathVariable String roomCdCi, @RequestBody EclassRoomDTO eclassRoomDTO) {
		
		/* eclassRoomService.updateEclassRoom(roomCdCi, eclassRoomDTO)을 호출하여
		   등록 정보가 담긴 DTO를 사용하여 강의실을 등록하고, 등록된 정보를 updatedRoom에 저장 */
		EclassRoomDTO updatedRoom = eclassRoomService.updateEclassRoom(roomCdCi, eclassRoomDTO);

		// 강의실 정보를 ResponseEntity로 감싸서 반환
		return new ResponseEntity<>(updatedRoom, HttpStatus.OK);
	}
}

2). Service

package tuna.tunaEclass.tunaEclass.eclassRoom.service;

import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import tuna.tunaEclass.tunaEclass.eclassRoom.dto.EclassRoomDTO;
import tuna.tunaEclass.tunaEclass.eclassRoom.dto.EclassRoomEntityBuilder;
import tuna.tunaEclass.tunaEclass.eclassRoom.entity.EclassRoomEntity;
import tuna.tunaEclass.tunaEclass.eclassRoom.repository.EclassRoomRepository;

import java.util.List;

@Service
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
public class EclassRoomService {

    private final EclassRoomRepository eclassRoomRepository;

    /* Test Class 생성 단축키 : Ctrl + Shift + T */
    /* 전체 조회, 단건 조회 = Entity -> DTO 변환
    *  등록, 수정 = DTO -> Entity 변환
    *  클라이언트 -> DB인지, DB -> 클라이언트인지 생각하면 됨 */

    /* 수정 */
    public EclassRoomDTO updateEclassRoom(String roomCdCi, EclassRoomDTO eclassRoomDTO) {
        
        /* 강의실 코드로 강의실을 조회한다, 해당 코드가 없으면 예외 발생 */
        EclassRoomEntity existingEntity = eclassRoomRepository.findById(roomCdCi)
                .orElseThrow(() -> new EntityNotFoundException("roomCdCi not found ! : " + roomCdCi));

        // DTO를 Entity로 변환
        EclassRoomEntity updatedEntity = EclassRoomEntityBuilder.buildFromDTO(eclassRoomDTO);

        // Entity 식별자를 설정
        updatedEntity.setRoomCdId(existingEntity.getRoomCdId());

        // 수정된 Entity를 DB에 저장하고, updatedEntity에 다시 할당
        updatedEntity = eclassRoomRepository.save(updatedEntity);

        return EclassRoomDTO.toDTO(updatedEntity); // Entity -> DTO 변환하여 반환
    }
    
}

(8). 삭제

1). Controller

@CrossOrigin("*")
@RestController
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
@Tag(name = "Eclass - Room")
public class EclassRoomController {

	private final EclassRoomService eclassRoomService; // 생성자 주입 방식

	/* @RestController이 적용되면 @ResponseBody를 사용하지 않아도 자동으로 됨 /*
	/* Test Class 생성 단축키 : Ctrl + Shift + T */

	/* 삭제 */
	@DeleteMapping("/DeleteEclassRoom/{roomCdCi}")
	@Operation(summary = "강의실 삭제", description = "강의실 삭제")
	// Void를 사용한 이유 = 해당 메서드가 반환하는데 반환할 데이터가 없음을 나타내기 위해
	public ResponseEntity<Void> deleteEclassRoom(@PathVariable String roomCdCi) {

		/* eclassRoomService.deleteEclassRoom(roomCdCi)를 호출하여
		해당되는 roomCdCi를 포함하는 강의실을 삭제합니다 */
		eclassRoomService.deleteEclassRoom(roomCdCi);
		return ResponseEntity.noContent().build();
	}
}

2). Service

@Service
@RequiredArgsConstructor // final을 가진 생성자 자동 생성
public class EclassRoomService {

    private final EclassRoomRepository eclassRoomRepository;

    /* Test Class 생성 단축키 : Ctrl + Shift + T */
    /* 전체 조회, 단건 조회 = Entity -> DTO 변환
    *  등록, 수정 = DTO -> Entity 변환
    *  클라이언트 -> DB인지, DB -> 클라이언트인지 생각하면 됨 */

    /* 삭제 */
    public void deleteEclassRoom(String roomCdCi) {
        /* 강의실 코드로 강의실을 조회한다, 해당 코드가 없으면 예외 발생 */
        EclassRoomEntity existingEntity = eclassRoomRepository.findById(roomCdCi) // findById(roomCdCi) = roomCdCi를 이용하여 조회
                .orElseThrow(() -> new EntityNotFoundException("roomCdCi not found ! : " + roomCdCi)); // roomCdCi가 없으면 예외

        // 엔터티 삭제
        eclassRoomRepository.delete(existingEntity); // .delete = 삭제
    }
}

4. Swagger 확인

5. PostMan을 이용하여 테스트 

클릭 !

 

[PostMan] API를 쉽게 테스트 해보자 !

1. PostMan이 뭐에요? 개발된 API를 테스트 할 수 있는 플랫폼 2. 개발 소스 클릭 ! [JPA] RESTful API를 만들어보자. (CRUD) 1. 환경 1. Sping Boot 2. Spring Data JPA 3. Gradle 4. postgreSQL 5. PostMan 6. Swagger 7. 인텔리제이

noorypapa.tistory.com

 

'Java > Spring Boot' 카테고리의 다른 글

[MSA] Spring Cloud로 MSA를 개발해보자 5편 [Config]  (1) 2024.08.26
[MSA] Spring Cloud로 MSA를 개발해보자 4편 [JWT 인증]  (0) 2024.08.25
[MSA] Spring Cloud로 MSA를 개발해보자 3편 [Gateway Service-2]  (0) 2024.08.16
[MSA] Spring Cloud로 MSA를 개발해보자 2편 [Gateway Service]  (1) 2024.08.13
[MSA] Spring Cloud로 MSA를 개발해보자 1편 [Service Discovery]  (0) 2024.08.11
  1. 1. 환경
  2. 2. DB 구조
  3. 3. CRUD 구현
  4. (1). Entity 생성
  5. (2). DTO 생성
  6. (3). DTO -> Entity 변환 클래스 생성
  7. (4). Repository 생성
  8. (4). 전체 조회
  9. (5). 단건 조회
  10. (6). 등록
  11. (7). 수정
  12. (8). 삭제
  13. 4. Swagger 확인
  14. 5. PostMan을 이용하여 테스트 
'Java/Spring Boot' 카테고리의 다른 글
  • [MSA] Spring Cloud로 MSA를 개발해보자 4편 [JWT 인증]
  • [MSA] Spring Cloud로 MSA를 개발해보자 3편 [Gateway Service-2]
  • [MSA] Spring Cloud로 MSA를 개발해보자 2편 [Gateway Service]
  • [MSA] Spring Cloud로 MSA를 개발해보자 1편 [Service Discovery]
누리는 귀여워
누리는 귀여워
안녕하세요, 누리 사료값을 벌기 위해 앞으로 나아가는 아버지의 개발 블로그입니다.
누리는 귀여워
누리아버지
누리는 귀여워
전체
오늘
어제
  • 분류 전체보기 (76)
    • Java (0)
      • Spring Boot (16)
      • JPA (1)
    • JavaScript (1)
      • React.js (1)
    • DataBase (3)
      • PostgreSQL (2)
    • DevOps (2)
      • MSA (1)
      • Docker (1)
    • OS (9)
    • Network (0)
    • CS 지식 (0)
    • Error 모음집 (0)
    • 코딩테스트 (32)
      • 기초 (31)
      • 알고리즘 (1)
    • 기타 (7)
    • 쉼터 (2)
      • 잡담 (2)
      • 하루 마무리 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 글쓰기

공지사항

인기 글

태그

  • postman
  • centos
  • eureka
  • Dockerfile
  • kafka connect
  • 인텔리제이
  • Docker
  • JPA
  • war 배포
  • Boot
  • Kafka
  • spring cloud
  • postgresql
  • 깃허브연동
  • GATEWAY
  • kafka sink connect
  • Oracle Cloud
  • spring boot
  • git bash
  • MSA

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.0
누리는 귀여워
[JPA] RESTful API를 만들어보자. (CRUD)
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.