[240320]-LEFTJOIN 조건이 NULL 일 때

XingXi·2024년 3월 19일
0

기록

목록 보기
19/33
post-thumbnail

새로운 유저를 생성하고 해당 유저로 로그인을 하면 해당 유저의 userPid 와 채팅방 리스트를 가지고 오는 기능을 개발중에 에러가 났다.

java.lang.NullPointerException: Cannot invoke "com.example.login.document.ChatMessage.getMsg()" because the return value of "com.example.login.document.ChatMessageRepository.findTopByRoomIdOrderByMsgSendingTimeDesc(String)" is null
	at com.example.login.service.LoginServiceImpl.lambda$loginHandler_v1$0(LoginServiceImpl.java:45) ~[main/:na]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
	at com.example.login.service.LoginServiceImpl.loginHandler_v1(LoginServiceImpl.java:38) ~[main/:na]
	at com.example.login.api.ApiLoginController.login_v1(ApiLoginController.java:23) ~[main/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]

ChatMessageRepository .findTopByRoomIdOrderByMsgSendingTimeDesc()
의 결과가 null 이어서 발생하는 예외이다.

1. 예외가 발생한 부분

    @Override
    public ChatUserDto.Login.Response loginHandler_v1(ChatUserDto.Login.Request req) {

        List<RoomInfoDto> roomInfoDtoList = new ArrayList<>();

        // EA
        String userPid = chatUserEntityRepository.getChatUserByEmail(req.getEmail()).orElseThrow().getPid();

        List<RoomIdCheckingCntDto> roomIdCheckingCntDtoList = chatUserEntityRepository.getRoomIdCheckingDtoListByUserPid(userPid);

        if(!roomIdCheckingCntDtoList.isEmpty()){

            roomIdCheckingCntDtoList.forEach(RoomIdCheckingCntDto -> {
                String roomPid = RoomIdCheckingCntDto.getRoomId();
                roomInfoDtoList.add(
                        RoomInfoDto.builder()
                                .roomPid(roomPid)
                                .checkingMsgCnt(RoomIdCheckingCntDto.getCheckingMessageCnt())
                                .currentChattingRoomMessageCnt(chatMessageRepository.countByRoomId(roomPid))
                                .latestMessage(chatMessageRepository.findTopByRoomIdOrderByMsgSendingTimeDesc(roomPid).getMsg())
                                .build());
            });
        }

latestMessage(chatMessageRepository
.findTopByRoomIdOrderByMsgSendingTimeDesc(roomPid)
.getMsg() 이 부분이 null 이다.

2. 문제가 되는 부분

  1. loginHandler_v1
List<RoomIdCheckingCntDto> roomIdCheckingCntDtoList = chatUserEntityRepository.getRoomIdCheckingDtoListByUserPid(userPid);

여기에서 사용하는 userPid 매개변수는 ChatUserEntity 의 필드중 하나로, 해당 필드는 TEST 용으로 생성이 되어 대화 내용이 존재하지 않는다는 것이다.

  1. 확인해보자
 System.out.println("roomIdCheckingCntDtoList.size() : "+roomIdCheckingCntDtoList.size());
        System.out.println("roomIdCheckingCntDtoList.get(0).getRoomId() : "+roomIdCheckingCntDtoList.get(0).getRoomId());
        System.out.println("roomIdCheckingCntDtoList.get(0).getCheckingMessageCnt() : "+roomIdCheckingCntDtoList.get(0).getCheckingMessageCnt());

확인해 보니 존재하지 않는 데이터인데 list 크기가 1이며 신기한 것은 해당
RoomIdCheckingCntDto 의 필드값들은 모두 null 이 나온다는 것이다.

3. 정말 원인이 되는 부분

    @Override
    public List<RoomIdCheckingCntDto> getRoomIdCheckingDtoListByUserPid(String pid) {
        return
                queryFactory.select(
                        Projections.bean(RoomIdCheckingCntDto.class,
                                chatRoomEntity.pid.as("roomId"),
                                chatWithUserEntity.checkingMessageCnt.as("checkingMessageCnt")
                        )
                  ).from(chatUserEntity)
                        .leftJoin(chatWithUserEntity).on(chatUserEntity.eq(chatWithUserEntity.chatUserEntity))
                        .leftJoin(chatRoomEntity).on(chatWithUserEntity.chatRoomEntity.eq(chatRoomEntity))
                        .where(chatUserEntity.pid.eq(pid))
                        .fetch();
    }

leftJoin(chatWithUserEntity).on(chatUserEntity.eq(chatWithUserEntity.chatUserEntity)) 여기에서 ChatUserEntity와 leftjoin 되는 chatwithUserEntity 가 존재하지 않아서 발생하는 예외 인것이다. 즉 leftJoin 대상이 존재하지 않아서 발생하는 예외였던 것이다.

수정 방법 1

    @Override
        List<RoomIdCheckingCntDto> resultList = new ArrayList<>();
        List<Tuple> queryResult = queryFactory
                .select(
                        chatRoomEntity.pid,
                        chatWithUserEntity.checkingMessageCnt
                )
                .from(chatUserEntity)
                .leftJoin(chatWithUserEntity)
                .on(chatUserEntity.eq(chatWithUserEntity.chatUserEntity))
                .leftJoin(chatRoomEntity)
                .on(chatWithUserEntity.chatRoomEntity.eq(chatRoomEntity))
                .where(chatUserEntity.pid.eq(pid))
                .fetch();

        for (Tuple tuple : queryResult) {
            String roomId = tuple.get(chatRoomEntity.pid);
            Long checkingMessageCnt = tuple.get(chatWithUserEntity.checkingMessageCnt);

            if (roomId != null && checkingMessageCnt != null) {
                resultList.add(new RoomIdCheckingCntDto(roomId, checkingMessageCnt));
            }
        }
        return resultList;
    }

해당 방법은 Repository 에서 JOIN 할 다른 Entity 의 null 여부를 확인하는 방법이다.
업로드중.. 다음과 같이 잘 작동 된다.

수정 방법 2

Repository

    @Override
    public List<RoomIdCheckingCntDto> getRoomIdCheckingDtoListByUserPid(String pid) {
        return
                queryFactory.select(
                        Projections.bean(RoomIdCheckingCntDto.class,
                                chatRoomEntity.pid.as("roomId"),
                                chatWithUserEntity.checkingMessageCnt.as("checkingMessageCnt")
                        )
                  ).from(chatUserEntity)
                        .leftJoin(chatWithUserEntity).on(chatUserEntity.eq(chatWithUserEntity.chatUserEntity))
                        .leftJoin(chatRoomEntity).on(chatWithUserEntity.chatRoomEntity.eq(chatRoomEntity))
                        .where(chatUserEntity.pid.eq(pid))
                        .fetch();
    }

serviceImpl

    @Override
    public ChatUserDto.Login.Response loginHandler_v1(ChatUserDto.Login.Request req) {

        List<RoomInfoDto> roomInfoDtoList = new ArrayList<>();

        // EA
        String userPid = chatUserEntityRepository.getChatUserByEmail(req.getEmail()).orElseThrow().getPid();

        List<RoomIdCheckingCntDto> roomIdCheckingCntDtoList = chatUserEntityRepository.getRoomIdCheckingDtoListByUserPid(userPid);
        roomIdCheckingCntDtoList.stream()
                .filter(dto -> dto.getRoomId() != null)  // RoomId가 null이 아닌 경우만 필터링
                .map(dto -> {
                    String roomPid = dto.getRoomId();
                    return RoomInfoDto.builder()
                            .roomPid(roomPid)
                            .checkingMsgCnt(dto.getCheckingMessageCnt())
                            .currentChattingRoomMessageCnt(chatMessageRepository.countByRoomId(roomPid))
                            .latestMessage(chatMessageRepository.findTopByRoomIdOrderByMsgSendingTimeDesc(roomPid).getMsg())
                            .build();
                })
                .forEach(roomInfoDtoList::add);

        System.out.println("userPid : "+userPid);
        System.out.println("roomInfoDtoList.size() : "+roomInfoDtoList.size());

        return ChatUserDto.Login.Response.builder()
                                        .userPid(userPid)
                                        .roomInfoDtoList(roomInfoDtoList)
                                        .build();
    }

1번과 달리 서비스 단에서 null 인지 확인하여 해결하였다.
이외에 다른 방법이 있으면 써봐야겠다.

0개의 댓글