-
프로젝트 그린더 Data Jpa 적용 2편Project/Greendar 2023. 4. 18. 22:08
1편에서 이어서 작성을 한다.
https://chosunghyun18.tistory.com/98
프로젝트 그린더 Data Jpa 적용 1편
이 글은 프로젝트의 일정은 끝났지만 개인의 학습과 탐구심의 목표를 채우기 위하여 이것 저것을 한 글 입니다. 관련 깃허브 : https://github.com/Team-Greendar/GreendarServer GitHub - Team-Greendar/GreendarServer: G
chosunghyun18.tistory.com
... +
https://github.com/Team-Greendar/GreendarServer/tree/develop/greendar
GitHub - Team-Greendar/GreendarServer: Greendar Server Repository
Greendar Server Repository. Contribute to Team-Greendar/GreendarServer development by creating an account on GitHub.
github.com
변경하고자 하는 기존의 Repo 코드이다. 전문은 상단 깃허브의 히스토리를 보면 된다.
.... @Repository @RequiredArgsConstructor public class EventTodoRepository { @PersistenceContext private final EntityManager em; public EventTodo save(Boolean complete, String imageUrl, EventTodoItem eventTodoItem, Member member) { EventTodo eventTodo= EventTodo.of(new TodoImage(imageUrl),complete,eventTodoItem,member); em.persist(eventTodo); return eventTodo; } public EventTodo updateEventTodoComplete(Long eventTodoId, Boolean complete) { EventTodo eventTodo = em.find(EventTodo.class, eventTodoId); eventTodo.updateComplete(complete); em.merge(eventTodo); return eventTodo; } public EventTodo updateEventTodoImageUrl(Long eventTodoId, String imageUrl) { EventTodo eventTodo = em.find(EventTodo.class, eventTodoId); eventTodo.updateImage(imageUrl); em.merge(eventTodo); return eventTodo; } ..... }
원하는 결과는 기존의 레포단에서 서비스 단에 제공하는 기능을 유지하고 최소한의 코드를 적는 법,
JPA Data 를 사용하는 것이다.
1. 인터페이스를 만들어주자. 기존 Repo class 에 인터페이스를 선언하고 접근자 와 body 를 전부 제거 해준다.
public interface EventTodoRepository { EventTodo save(Boolean complete, String imageUrl, EventTodoItem eventTodoItem, Member member); EventTodo updateEventTodoComplete(Long eventTodoId, Boolean complete); EventTodo updateEventTodoImageUrl(Long eventTodoId, String imageUrl) ; EventTodo findOneByEventTodoItemIdMemberId(Long memberId, Long eventTodoItemId); List<EventTodo> findAllEventTodoByMember(Long memberId); List<EventTodoResponseDto> findAllByDay(LocalDate day, Member member); List<EventTodoResponseDto> findAllByMonth(LocalDate date, Member member); }
2. 구현 부분을 작성 : 기존의 코드에서 복 붙이다. 구현 부분이 필요 없을 수 도 있지만 혹시나 잘못된경우를 생각하여 만들었다 .
(data JPA 를 적용하고 확인하면 삭제를 해주자)
.. @Repository @RequiredArgsConstructor public class EventTodoJpaRepository implements EventTodoRepository { @PersistenceContext private final EntityManager em; public EventTodo save(Boolean complete, String imageUrl, EventTodoItem eventTodoItem, Member member) { EventTodo eventTodo= EventTodo.of(new TodoImage(imageUrl),complete,eventTodoItem,member); em.persist(eventTodo); return eventTodo; } public EventTodo updateEventTodoComplete(Long eventTodoId, Boolean complete) { EventTodo eventTodo = em.find(EventTodo.class, eventTodoId); eventTodo.updateComplete(complete); em.merge(eventTodo); return eventTodo; } .. public EventTodo findOneByEventTodoItemIdMemberId(Long memberId, Long eventTodoItemId) { try { return em.createQuery("select e from EventTodo e " + "where e.member.id =:memberId and e.eventTodoItem.id =:eventTodoItemId" , EventTodo.class) .setParameter("memberId", memberId) .setParameter("eventTodoItemId", eventTodoItemId) .getSingleResult(); } catch (NoResultException nre) { return null; } } .. }
3. 메서드 명을 dataJPA 에 맞게 변경 및 testcode 작성을 해주자
Spring Data Jpa 를 DB Update 를 하고자 하면 Udate 쿼리를 사용하거나 jpa 의 변경 감지를 이용하는 방식으로 업데이트를 한다.
model 에 변경에 관한 코드를 추가해준후 service layer 에서 호출을 해준다.
4 . data JPA 적용 + 복잡한 쿼리 옮겨주기
기존 코드 : Service Layer , 사용자가 체크박스를 체크를 했을때 , 없으면 생성을 하고 있으면 업데이트를 하는 기능이다.
@Transactional public EventTodo updateEventTodo(Boolean complete , String imageUrl, Long eventTodoItemId,String token) { Member member = memberRepository.fineOneByToken(token); EventTodoItem eventTodoItem = eventTodoItemRepository.findOneById(eventTodoItemId); EventTodo eventTodo = eventTodoRepository.findOneByEventTodoItemIdMemberId(member.getId(),eventTodoItem.getId()); // 생성 if(eventTodo==null) { if(complete == null) { return eventTodoRepository.save(false,imageUrl,eventTodoItem,member); } else { return eventTodoRepository.save(true,"EMPTY",eventTodoItem,member); } }else{ if(complete == null) { return eventTodoRepository.updateEventTodoImageUrl(eventTodo.getId(),imageUrl); } else { return eventTodoRepository.updateEventTodoComplete(eventTodo.getId(),complete); } } }
1차 변경 후 코드이다.
Service Layer :
@Transactional public EventTodo updateEventTodo(Boolean complete , String imageUrl, Long eventTodoItemId,String token) { Member member = memberRepository.fineOneByToken(token); EventTodoItem eventTodoItem = eventTodoItemRepository.findOneById(eventTodoItemId); EventTodo eventTodo = eventTodoRepository.findByEventTodoItemIdAndMemberId(eventTodoItem.getId(),member.getId()) .orElseThrow(() -> new EntityNotFoundException("EventTodo not found w" )); // 생성 if(eventTodo==null) { if(complete == null) { EventTodo newEventTodo = EventTodo.of(new TodoImage(imageUrl),false,eventTodoItem,member); return eventTodoRepository.save(newEventTodo); } else { EventTodo newEventTodo = EventTodo.of(new TodoImage("EMPTY"),true,eventTodoItem,member); return eventTodoRepository.save(newEventTodo); } }else{ if(complete == null) { return eventTodo.updateImage(imageUrl); } else { return eventTodo.updateComplete(complete); } } }
위의 변경 후의 Service 코드는 2 % 부족하다.
Optional 을 사용하기위하여 Data jpa 를 적용했으니 다음과 같이 변경하자.
@Transactional public EventTodo updateEventTodo(Boolean complete, String imageUrl, Long eventTodoItemId, String token) { Member member = memberRepository.findOneByToken(token); EventTodoItem eventTodoItem = eventTodoItemRepository.findOneById(eventTodoItemId); EventTodo eventTodo = eventTodoRepository.findByEventTodoItemIdAndMemberId(eventTodoItem.getId(), member.getId()) .orElse(EventTodo.of(new TodoImage(imageUrl), complete != null && complete, eventTodoItem, member)); if (complete != null) { eventTodo.updateComplete(complete); } else { eventTodo.updateImage(imageUrl); } return eventTodoRepository.save(eventTodo); }
변경 후의 Repo code 는 파라미터를 받아서 바로 쿼리에 넣는것이기 때문에, 파라미터가 많아졌다.
@Repository public interface EventTodoRepository extends JpaRepository<EventTodo,Long> { Optional<EventTodo> findByEventTodoItemIdAndMemberId(Long eventTodoItemId, Long memberId); @Query("select new greendar.domain.eventtodo.dto.EventTodoResponseDto(e) "+ "from EventTodo e " + "where e.eventTodoItem.date = :oneDay and e.member.id = :memberId " + "order by e.eventTodoItem.date desc") List<EventTodoResponseDto> findAllByDay(@Param("oneDay")LocalDate day, @Param("memberId") Long memberId); @Query("select new greendar.domain.eventtodo.dto.EventTodoResponseDto(e) " + "from EventTodo e " + "where e.member.id=:memberId and e.eventTodoItem.date between :startDate and :endDate " + "order by e.eventTodoItem.date desc") List<EventTodoResponseDto> findAllByMonth(@Param("startDate")LocalDate startDate, @Param("endDate")LocalDate endDate, @Param("memberId") Long memberId); }
변경전 :
@Repository @RequiredArgsConstructor public class EventTodoRepository { @PersistenceContext private final EntityManager em; public EventTodo save(Boolean complete, String imageUrl, EventTodoItem eventTodoItem, Member member) { EventTodo eventTodo= EventTodo.of(new TodoImage(imageUrl),complete,eventTodoItem,member); em.persist(eventTodo); return eventTodo; } public EventTodo updateEventTodoComplete(Long eventTodoId, Boolean complete) { EventTodo eventTodo = em.find(EventTodo.class, eventTodoId); eventTodo.updateComplete(complete); em.merge(eventTodo); return eventTodo; } public EventTodo updateEventTodoImageUrl(Long eventTodoId, String imageUrl) { EventTodo eventTodo = em.find(EventTodo.class, eventTodoId); eventTodo.updateImage(imageUrl); em.merge(eventTodo); return eventTodo; } public EventTodo findOneByEventTodoItemIdMemberId(Long memberId, Long eventTodoItemId) { try { return em.createQuery("select e from EventTodo e " + "where e.member.id =:memberId and e.eventTodoItem.id =:eventTodoItemId" , EventTodo.class) .setParameter("memberId", memberId) .setParameter("eventTodoItemId", eventTodoItemId) .getSingleResult(); } catch (NoResultException nre) { return null; } } public List<EventTodo> findAllEventTodoByMember(Long memberId) { return em.createQuery("select e from EventTodo e " + " where e.member.id = :memberId " + "order by e.eventTodoItem.date desc" , EventTodo.class) .setParameter("memberId", memberId) .getResultList(); } public List<EventTodoResponseDto> findAllByDay(LocalDate day, Member member){ return em.createQuery("select new greendar.domain.eventtodo.dto.EventTodoResponseDto(e) "+ "from EventTodo e " + "where e.eventTodoItem.date = :oneDay and e.member.id = :memberId " + "order by e.eventTodoItem.date desc" , EventTodoResponseDto.class) .setParameter("oneDay",day) .setParameter("memberId",member.getId()) .getResultList(); } public List<EventTodoResponseDto> findAllByMonth(LocalDate date, Member member) { YearMonth month = YearMonth.from(date); LocalDate start = month.atDay(1); LocalDate end = month.atEndOfMonth(); return em.createQuery("select new greendar.domain.eventtodo.dto.EventTodoResponseDto(e) " + "from EventTodo e " + "where e.member.id=:memberId and e.eventTodoItem.date between :startDate and :endDate " + "order by e.eventTodoItem.date desc",EventTodoResponseDto.class) .setParameter("startDate",start) .setParameter("endDate",end) .setParameter("memberId",member.getId()) .getResultList(); } }
SpringBootData JPA 를 이용하면 확실하게 코드 량이 줄어들고 도메인 model의 추가 로직이 필요한점 , 명세를 정확하게 이해를 하고 내부 동작을 이해를 해야한다.
'Project > Greendar' 카테고리의 다른 글
처음으로 회원가입시 생기는 문제 (0) 2023.04.21 프로젝트 그린더 Data Jpa 적용 1편 (0) 2023.04.18