글 수정 API 만들기
Article 코드 추가
// 객체의 상태값 수정
public void update(String title, String content) {
// 유효성 검사 반드시 진행해야 함.
// 즉, 데이터가 엔티티에 저장되기 전에 반드시 검증
if(title == null || title.trim().isEmpty()) {
throw new Exception400("제목은 null이거나 빈 문자열일 수 없습니다.");
}
if(content == null || content.trim().isEmpty()) {
throw new Exception400("내용은 null이거나 빈 문자열일 수 없습니다.");
}
this.title = title;
this.content = content;
}
유효성 검사를 통과한 후 title과 content 필드의 값을 업데이트한다.
도메인 모델 - 현실 세계의 중요한 개념을 코드로 나타낸 것 (게시글, 사용자, 댓글, 주문, 상품)
객체 스스로 자신의 상태를 관리하도록 한다 - 자신의 데이터와 행동에 책임을 진다.
일단은 application-dev.yml에서 datasource의 url을 변경하고
datasource:
url: jdbc:mysql://localhost:3306/jpa_demo?useSSL=false&serverTimezone=Asia/Seoul&useLegacyDatetimeCode=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: MySQL 이름
password: MySQL 비밀번호
build.gradle에서 아래의 코드가 의존성 주입이 되어 있는지 확인한다.
주석 처리 되어 있을 시 해제한다.
runtimeOnly 'com.mysql:mysql-connector-j'
h2-console 말고 MySQL을 실행시켜 아래와 같이 코드를 실행한다.
create database jpa_demo;
use jpa_demo;
select * from tb_article;
위와 같이 postman에서
Post 방식으로 Send한 후
데이터를 insert 하고,
MySQL에서
select 문을 실행하면
아래와 같은 결과를 확인할 수 있다.
실행결과
BlogService 코드 추가
수정기능에 @Transactional 처리 하기
JpaRepository 메서드인 save()나 delete()를 직접 사용 했었음.
이 메서드들은 이미 트랜잭션 처리되어 있음.
따라서 서비스 계층에서 추가로 트랜잭션을 선언할 필요가 없었음.
// 수정 비지니스 로직
// 영속성 컨텍스트에서 또는 DB 존재하는 Article 엔티티(row)를 가지고 와서(영속성 컨텍스트에 이미 존재함)
// 상태값을 수정하고 그 결과를 호출한 곳으로 반환하다.
@Transactional
public Article update(Integer id, ArticleDTO dto) {
// 수정 로직
Article articleEntity = postRepository.findById(id).orElseThrow(() -> new Exception400("not found: " + id));
// 객체 상태 값 변경
articleEntity.update(dto.getTitle(), dto.getContent());
// 변경된 사항을 DB에 save 처리
// postRepository.save(articleEntity);
return articleEntity;
}
코드 해석
public Article update(Integer id, ArticleDTO dto){ }
매개변수로 받는 id는 수정할 Article의 고유 식별자이며, dto는 업데이트에 필요한 데이터를 담고 있는 데이터 전송 객체다.
Article articleEntity = postRepository.findById(id).orElseThrow(() -> new Exception400("not found: " + id));
postRepository.findById(id)를 사용하여 DB에 주어진 id에 해당하는 Article 엔티티를 조회한다.
orElseThrow() 메서드는 만약 해당 id로 찾은 결과가 없을 경우 Exception400을 던지며 메시지를 반환하여 유효하지 않은 id에 대해 적절한 예외 처리를 한다.
articleEntity.update(dto.getTitle(), dto.getContent());
Article 엔티티의 update 메서드 호출하여 제목과 내용을 dto에서 가져온 값으로 업데이트 한다.
// postRepository.save(articleEntity);
주석처리 되어 있는 이유
save() 메서드는 새로운 엔티티를 저장하거나 기존 엔티티를 업데이트하는 데 사용되지만,
이미 영속성 컨텍스트에서 관리되고 있는 객체이기 때문에 호출할 필요가 없다.
영속성 컨텍스트 - 더티 체킹
영속성 컨텍스트에 있는 객체의 상태가 변경되었을 때,
트랜잭션이 커밋될 때 변경 사항을 자동으로 데이터베이스에 반영하는 Hibernate의 기능을 의미한다.
레포지토리의 save() 메서드는 수정할 때도 사용 가능하다.
단, 호출하지 않은 이유는 더티 체킹 동작 때문이다.
즉, 트랜잭션 커밋 시 자동으로 영속성 컨텍스트와 데이터베이스(DB)에 변경 사항이 반영된다
트랜잭션 사용의 일반적인 규칙은 서비스 메서드가 여러 데이터베이스 작업을 포함하거나, 영속성 컨텍스트를 통해 엔티티 변경 사항을 추적해야 하는 경우 @Transactional을 사용하여 해당을 수행 한다.
트랜잭션과 영속성 컨텍스트의 관계
- 트랜잭션이 시작되면 영속성 컨텍스트도 활성화된다.
- 트랜잭션 내에서 조회된 엔티티는 영속성 컨텍스트에서 관리되는 영속 상태가 된다.
더티 체킹의 메커니즘
- 엔티티의 필드 값을 변경하면 영속성 컨텍스트가 이를 감지합니다.
- 변경된 엔티티는 트랜잭션 커밋 시 DB에 자동으로 반영됩니다.
save() 메서드의 필요성
- 영속 상태의 엔티티는 save()를 호출하지 않아도 변경 사항이 DB에 반영됩니다. 준영속 상태(detached)의 엔티티나
- 트랜잭션이 없는 경우에는 save()를 사용하여 변경 사항을 저장해야 합니다.
코드의 효율성
불필요한 save() 호출을 줄임
코드 실행해서 update 하기
PUT 형식으로
데이터를 send 한 후
MySQL에서
select 문으로 조회하면
아래와 같이 id 1번의
content와 title이 수정된 것을
확인할 수 있다.
추가 정보
데이터 바인딩
HTTP 요청에서 전달된 데이터를 서버 측의 자바 객체나 메서드 파라미터에 자동으로 변환하고 할당하는 과정을 말합니다. 이를 통해 개발자는 복잡한 데이터 추출 및 변환 로직을 직접 구현하지 않고도 간편하게 데이터를 사용할 수 있습니다.
- DispatcherServlet:
- Spring MVC의 프론트 컨트롤러(Front Controller) 역할을 합니다.
- 모든 HTTP 요청을 받아 적절한 컨트롤러(Controller)로 전달합니다.
- 요청 처리 과정의 중앙 허브로, 요청의 라우팅 및 데이터 바인딩을 조율합니다.
- HandlerMapping:
- 요청 URL과 HTTP 메서드에 따라 적절한 컨트롤러 메서드를 매핑합니다.
- 예를 들어, @PutMapping("/api/articles/{id}")와 같은 매핑 정보를 바탕으로 해당 요청을 처리할 메서드를 찾습니다.
- HandlerAdapter:
- 매핑된 컨트롤러 메서드를 호출하고, 필요한 인자를 제공하는 역할을 합니다.
- HandlerMethodArgumentResolver를 사용하여 메서드 파라미터에 데이터를 바인딩합니다.
- HandlerMethodArgumentResolver:
- 컨트롤러 메서드의 파라미터에 데이터를 바인딩하기 위한 전략을 정의합니다.
- 대표적인 구현체로는 RequestParamMethodArgumentResolver, PathVariableMethodArgumentResolver, RequestBodyMethodArgumentResolver 등이 있습니다.
- HttpMessageConverter:
- HTTP 요청의 바디에 담긴 데이터를 자바 객체로 변환하거나, 자바 객체를 HTTP 응답의 바디로 변환하는 역할을 합니다.
- Jackson 라이브러리를 사용하여 JSON 데이터를 자바 객체로 변환하는 MappingJackson2HttpMessageConverter가 대표적입니다.