
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=falseJPA 포함 사용시 프로젝트 구조
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