새로운 유저를 생성하고 해당 유저로 로그인을 하면 해당 유저의 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 이어서 발생하는 예외이다.
@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 이다.
List<RoomIdCheckingCntDto> roomIdCheckingCntDtoList = chatUserEntityRepository.getRoomIdCheckingDtoListByUserPid(userPid);
여기에서 사용하는
userPid
매개변수는 ChatUserEntity 의 필드중 하나로, 해당 필드는 TEST 용으로 생성이 되어 대화 내용이 존재하지 않는다는 것이다.
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 이 나온다는 것이다.
@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 대상이 존재하지 않아서 발생하는 예외였던 것이다.
@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 여부를 확인하는 방법이다.
다음과 같이 잘 작동 된다.
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 인지 확인하여 해결하였다.
이외에 다른 방법이 있으면 써봐야겠다.