ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프로젝트 그린더 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
Designed by Tistory.