[web, service, repository, Dto, Domain 5가지 레이아웃 비지니스 처리를 담당하는 곳은?]
Domain으로 기존에 서비스를 처리하던 방식을 트랜잭션 스크립트라고 한다.
3. 도메인 모델
서비스 메소드는 트랜잭션과 도메인 간의 순서만 보장해주어야 한다. 이를 유의해서 우리는 도메인 모델을 다루고 코드를 작성할 것이다.
[디렉토리 현황]
[등록]
[Controller class]
package com.yeomyaloo.book.springboot.web;
import com.yeomyaloo.book.springboot.service.posts.PostsService;
import com.yeomyaloo.book.springboot.web.dto.PostsUpdateRequestDto;
import com.yeomyaloo.book.springboot.web.dto.PostsResponseDto;
import com.yeomyaloo.book.springboot.web.dto.PostsSaveRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
@RestController
public class PostsApiController {
//final 키워드가 붙어서 꼭 값을 돌려주어야 한다!
private final PostsService postsService;
@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto){
return postsService.save(requestDto);
}
[Service class]
package com.yeomyaloo.book.springboot.service.posts;
import com.yeomyaloo.book.springboot.web.dto.PostsUpdateRequestDto;
import com.yeomyaloo.book.springboot.web.dto.PostsResponseDto;
import com.yeomyaloo.book.springboot.web.dto.PostsSaveRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.yeomyaloo.book.springboot.domain.posts.Posts;
import com.yeomyaloo.book.springboot.domain.posts.PostsRepository;
import javax.transaction.Transactional;
// @Autowired 없이 생성자 주입이 되는 이유는??
// final이 선언된 모든 필드를 인자값으로 하는 생성자를 롬복의 @RequiredArgsConstructor가 대신 생성해준 것.
//그렇다면 왜 이렇게 생성자 주입을 직접 쓰지 않고 어노테이션을 사용해서 진행하는가?? -> 의존성 관계 변경때마다 생성자 코드를 계속해서 수정하는 번거로움을 피하기 위해서다.
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}
@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto) {
Posts posts = postsRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("해당 게시글이 없습니다. id = " + id));
posts.update(requestDto.getTitle(),requestDto.getContent());
return id;
}
public PostsResponseDto findById(Long id) {
Posts entity = postsRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("해당 게시글이 없습니다. id = " +id));
return new PostsResponseDto(entity);
}
}
update 기능에서 데이터베이스에 쿼리를 날리는 부분이 없다. 이게 가능한 이유는 JPA의 영속성 컨텍스트 때문이다.
영속성 컨텍스트란?
엔티티를 영구 저장하는 환경으로 일종의 논리적인 개념으로 JPA의 핵심 내용은 엔티티가 영속성 컨텍스트에 포함되어 있냐 아니냐로 갈리게 된다.
JPA 엔티티 매니저가 활성화된 상태로 트랜잭션 안에서 데이터베이스에서 데이터를 가져오면 이 데이터는 영속성 컨텍스트가 유지된 상태이다.
이 상태에서 해당 데이터의 값을 변경하면 트랜잭션이 끝나는 시점에 해당 테이블에 변경분을 반영한다. 즉, Entity 객체의 값만 변경하면 별도로 update 쿼리를 날릴 필요가 없다는 것이다. 이를 더티 체킹이라고 한다.
[Service class(추가)]
package com.yeomyaloo.book.springboot.service.posts;
import com.yeomyaloo.book.springboot.web.dto.PostsUpdateRequestDto;
import com.yeomyaloo.book.springboot.web.dto.PostsResponseDto;
import com.yeomyaloo.book.springboot.web.dto.PostsSaveRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.yeomyaloo.book.springboot.domain.posts.Posts;
import com.yeomyaloo.book.springboot.domain.posts.PostsRepository;
import javax.transaction.Transactional;
// @Autowired 없이 생성자 주입이 되는 이유는??
// final이 선언된 모든 필드를 인자값으로 하는 생성자를 롬복의 @RequiredArgsConstructor가 대신 생성해준 것.
//그렇다면 왜 이렇게 생성자 주입을 직접 쓰지 않고 어노테이션을 사용해서 진행하는가?? -> 의존성 관계 변경때마다 생성자 코드를 계속해서 수정하는 번거로움을 피하기 위해서다.
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;
@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}
@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto) {
Posts posts = postsRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("해당 게시글이 없습니다. id = " + id));
posts.update(requestDto.getTitle(),requestDto.getContent());
return id;
}
public PostsResponseDto findById(Long id) {
Posts entity = postsRepository.findById(id)
.orElseThrow(()->new IllegalArgumentException("해당 게시글이 없습니다. id = " +id));
return new PostsResponseDto(entity);
}
}
[로컬환경에서 데이터베이스를 직접 접근하는 방법(H2 콘솔로 들어가자)]
1. 웹 콘솔
http://localhost:8080/h2-console로 들어가면 위의 이미지와 같이 웹 콘솔 화면이 등장합니다.
이때 JDBC URL이 앞 화면과 같이 설정되었나를 확인하고 저렇게 치고 들어가주세요 .
2. 관리 화면
POSTS 테이블이 정상적으로 노출되어야 합니다.
간단한 쿼리를 실행합니다 (SELECT * FROM POSTS )
등록된 데이터가 현재까지는 없기 때문에 간단한게 insert 쿼리를 실행해보고 이를 API로 조회해 보도록 합시다.
insert 쿼리 실행
insert into posts(author,content,title) values ('author','content','title');
브라우저로 API 조회
API를 조회 기능을 테스트 했습니다.
이때 정렬된 JSON 형태가 보고 싶다면 크롬에서 JSON Viewer라는 플러그인을 설치해주시면 됩니다. (본인은 설치하지 않음)