javaspring-bootjpa

New chat message replacing first message instead of adding to end


I'm working on a chat application using Spring Boot, and I'm facing an issue with managing chat messages when the chat reaches its maximum size. My goal is to implement a system where, once the maximum number of messages is reached, the oldest message is removed to make room for a new one. However, I'm encountering a problem where the new message seems to replace the first message in the list instead of being added to the end.

Here’s the relevant part of my ChatMessageServiceImpl class:

@Service
@RequiredArgsConstructor
public class ChatMessageServiceImpl implements ChatMessageService {

    private final ChatRepository chatRepository;
    private final UserService userService;

    @Value("${monopoly.app.chat.max-size}")
    private Integer maxSize;

    @Override
    public ChatMessage save(Chat chat, ChatMessageDto chatMessageDto) {
        ChatMessage chatMessage = ChatMessage.builder()
                .sender(userService.findByUsername(chatMessageDto.getSender()))
                .content(chatMessageDto.getContent())
                .timestamp(LocalDateTime.now())
                .receiver(chatMessageDto.getReceiver() != null ?
                        userService.findByUsername(chatMessageDto.getReceiver()) : null)
                .chat(chat)
                .build();

        // Check if the chat has reached the max size
        if (chat.getMessages().size() >= maxSize) {
            chat.getMessages().remove(0); // Remove the first message
        }

        chat.getMessages().add(chatMessage); // Add the new message to the end

        chatRepository.save(chat);
        return chatMessage;
    }
}

When the chat reaches the maximum size, the new message is supposed to be added to the end of the list, and the oldest message should be removed. Instead, it appears that the new message replaces the first message in the list.

Here's Chat class if you have questions about how messages are stored:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
@Entity
@Table(name = "chats")
public class Chat {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Integer unreadMessages = 0;

    @OneToMany(mappedBy = "chat", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<ChatMessage> messages = new ArrayList<>();
}
List<ChatMessage> messages = chat.getMessages();
if (messages.size() >= maxSize) {
    messages.remove(0);
}
messages.add(chatMessage);
chat.setMessages(messages);

What might be causing the new message to replace the first message instead of being added to the end? Are there any potential issues with how the list or the database is being handled in this situation?


Solution

  • You're not specifying an order, so you get whatever comes out of the db. Add a @OrderBy("fieldName") under the @OneToMany on Chat:

    @OneToMany(mappedBy = "chat", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    @OrderBy("fieldName")
    private List<ChatMessage> messages = new ArrayList<>();
    

    Replace fieldName with the appropriate field, possibly the timestamp it seems to have based on your builder code.