본문 바로가기
프로젝트

대화방 생성 - 중복된 sql 쿼리문

by backwards 2023. 6. 12.

요약

  1. member관련 중복된 쿼리 문제를 해결하였다.
  2. 각각 작동하던 메서드를 하나의 트랜잭션으로 묶어주었다.

 


 

문제점

  1. 처음에 member관련 쿼리가 2번 발생함
    • username을 select하는 쿼리, memberId를 select하는 쿼리
    • username관련 쿼리는 jwt인증관련해서 나온 쿼리였음
    • memberId관련 쿼리는 conversationService의 addMember 메서드 때문에 발생한 쿼리였음
      • @AuthenticationPrincipal 때문인 줄 알았으나, 해당 애너테이션은 SecurityContext에서 member객체를 가져오는 것이며 DB관련 쿼리와는 관련없음
  2. PostConversation의 반환값으로 conversation뿐만 아니라 categoryList도 필요하다. 지금은 service단에서 conversation과 categoryList를 따로 가공해서 반환한다. 전체의 과정이 하나의 트랜잭션으로 묶이는 것이 맞다고 판단했고 따라서 controller에서 메서드 두번을 사용하던 것을 한 번으로 수정해야한다.

수정 전 생각

  1. conversation을 인스턴스화 하고, memberID를 추가해주고, conversationId로 conversation을 찾아서 requestAnswer메서드 매개변수로 conversation을 넣어준다.
  2. conversation과 category를 아우르는 새로운 클래스를 만든다.
    • 처음에는 service단에서만 처리해서 반환한다. → conversation과 category 전혀 다른 엔티티를 한 번에 반환할 수는 없다.

과정

1

대화 세션을 생성하면 다음과 같은 sql쿼리가 발생한다.

 


//-------------------------해결할 부분-------------------------
// 1
Hibernate: 
    select
        memberenti0_.id as id1_5_,
        memberenti0_.avatar_link as avatar_l2_5_,
        memberenti0_.created_at as created_3_5_,
        memberenti0_.password as password4_5_,
        memberenti0_.user_id as user_id5_5_,
        memberenti0_.username as username6_5_ 
    from
        member memberenti0_ 
    where
        memberenti0_.username=?
// 2
Hibernate: 
    select
        memberenti0_.id as id1_5_0_,
        memberenti0_.avatar_link as avatar_l2_5_0_,
        memberenti0_.created_at as created_3_5_0_,
        memberenti0_.password as password4_5_0_,
        memberenti0_.user_id as user_id5_5_0_,
        memberenti0_.username as username6_5_0_ 
    from
        member memberenti0_ 
    where
        memberenti0_.id=?
//-----------------------------------------------------------------       
       
Hibernate: 
    insert 
    into
        conversation
        (id, activity_level, answer_summary, bookmarked, created_at, delete_status, member_id, modified_at, pinned, published, saved, tagged, title, view_count) 
    values
        (default, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    select
        qna0_.id as id1_6_,
        qna0_.answer as answer2_6_,
        qna0_.bookmark_status as bookmark3_6_,
        qna0_.conversation_id as conversa5_6_,
        qna0_.question as question4_6_ 
    from
        qna qna0_ 
    where
        qna0_.conversation_id=?
Hibernate: 
    insert 
    into
        qna
        (id, answer, bookmark_status, conversation_id, question) 
    values
        (default, ?, ?, ?, ?)

// requestAnswer 메서드 끝

Hibernate: 
    update
        conversation 
    set
        activity_level=?,
        answer_summary=?,
        bookmarked=?,
        created_at=?,
        delete_status=?,
        member_id=?,
        modified_at=?,
        pinned=?,
        published=?,
        saved=?,
        tagged=?,
        title=?,
        view_count=? 
    where
        id=?
Hibernate: 
    select
        category0_.id as id1_1_,
        category0_.member_id as member_i2_1_,
        category0_.name as name3_1_ 
    from
        category category0_ 
    where
        category0_.member_id=? 
        and (
            category0_.id not in  (
                ?
            )
        )

 

다른 쿼리문들을 좀 더 최적화 할 수 있을지는 잘 모르겠지만, 일단 제일 처음의 member관련 쿼리가 중복되는 문제는 지금 충분히 해결할 수 있다.

 

username으로 select하는 첫 번째 쿼리문은 jwt로 Authentication을 설정하는 필터에서 발생하였고, 

memberId로 select하는 두 번째 쿼리문은 대화세션을 생성하는 service클래스의 메서드에서 발생하였다. 그 메서드 아래와 같다.

 

@Transactional
public Conversation createConversation(long memberId, QnADto.Post dto)
{
    Conversation conversation = new Conversation();
    conversation.addMember(memberRepository.findById(memberId).orElse(null)); // 이 부분
    Conversation savedConversation = conversationRepository.save(conversation);

    long conversationId = savedConversation.getId();
    dto.setConversationId(conversationId);
    qnaService.requestAnswer(dto);

    return conversationRepository.save(conversation);
}

 

member엔티티와 conversation엔티티는 부모-자식 관계이기 때문에 addMember메서드로 member객체를 찾아 추가해주었는데, 이 부분때문에 중복된 쿼리가 발생하는 것이었다.

 

현 로직에서 member객체에서 필요한 정보는 없으며, 만약 있더라도 SecurityContext에서 불러오면 굳이 DB를 사용할 필요가 없다.

 

수정한 코드는 다음과 같다.

 

public Conversation createConversation(long memberId, ConversationDto.Post dto)
{
    Conversation conversation = new Conversation();
    conversation.addMember(new MemberEntity(memberId)); // 수정
    Conversation savedConversation = conversationRepository.save(conversation);

    dto.setConversation(savedConversation);
    qnaService.requestAnswer(savedConversation, dto.getQuestion());

    return conversationRepository.save(conversation);
}

 


2

    @PostMapping
    public ResponseEntity generateConversation(@RequestBody ConversationDto.Post dto,
                                               @AuthenticationPrincipal MemberEntity member)
    {
        Long memberId = member.getId();

        Conversation savedConversation = conversationService.createConversation(memberId, dto);

        ConversationDto.Response response = conversationService.getConversationAndCategoryList(savedConversation, memberId);
        
        return new ResponseEntity<>(response, HttpStatus.CREATED);
    }

 

controller에서 service 메서드를 각각 호출하고 있다.

새로운 대화세션을 생성하는 기능을 하나의 트랜잭션으로 만들고 싶어 아래와 같이 코드를 수정하였다.

 

    @PostMapping
    public ResponseEntity generateConversation(@RequestBody ConversationDto.Post dto,
                                               @AuthenticationPrincipal MemberEntity member)
    {
        Long memberId = member.getId();
        
        ConversationDto.Response response = conversationService.createConversation(memberId, dto);
        
        return new ResponseEntity<>(response, HttpStatus.CREATED);
    }
    
     ---
     
    @Transactional
    public ConversationDto.Response createConversation(long memberId, ConversationDto.Post dto)
    {
    	...
    }

 

controller에서는 하나의 메서드만 호출하고, service클래스에서는 @transactional 애너테이션을 추가하여 해당 기능이 하나의 트랜잭션으로 동작하도록 하였다.

 

  •  

'프로젝트' 카테고리의 다른 글

JPA n+1 문제  (0) 2023.06.21
지연 로딩(Lazy loading), 영속성 컨텍스트  (0) 2023.06.18
대화방 제목/pin 수정 - fetchType과 DTO  (0) 2023.06.14
프로젝트 개선  (0) 2023.06.09