-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: message entity, repository 구현 #424
base: BE/develop
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.yigongil.backend.domain.message; | ||
|
||
import com.yigongil.backend.domain.BaseEntity; | ||
import com.yigongil.backend.domain.member.Member; | ||
import javax.persistence.Column; | ||
import javax.persistence.Entity; | ||
import javax.persistence.GeneratedValue; | ||
import javax.persistence.GenerationType; | ||
import javax.persistence.Id; | ||
import javax.persistence.JoinColumn; | ||
import javax.persistence.ManyToOne; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@Entity | ||
public class Message extends BaseEntity { | ||
|
||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Id | ||
private Long id; | ||
|
||
@ManyToOne | ||
@JoinColumn(nullable = false, updatable = false) | ||
private Member sender; | ||
|
||
@ManyToOne | ||
@JoinColumn(nullable = false, updatable = false) | ||
private Member receiver; | ||
|
||
@Column(nullable = false) | ||
private String content; | ||
|
||
protected Message() { | ||
} | ||
|
||
@Builder | ||
public Message(Long id, Member sender, Member receiver, String content) { | ||
this.id = id; | ||
this.sender = sender; | ||
this.receiver = receiver; | ||
this.content = content; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Message{" + | ||
"id=" + id + | ||
", sender=" + sender + | ||
", receiver=" + receiver + | ||
", content='" + content + '\'' + | ||
'}'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.yigongil.backend.domain.message; | ||
|
||
public interface MessageListDto { | ||
|
||
String getContent(); | ||
|
||
String getCreatedAt(); | ||
|
||
boolean getIsMine(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.yigongil.backend.domain.message; | ||
|
||
import com.yigongil.backend.domain.member.Member; | ||
import java.util.List; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.data.jpa.repository.Query; | ||
import org.springframework.data.repository.Repository; | ||
|
||
public interface MessageRepository extends Repository<Message, Long> { | ||
|
||
Message save(Message message); | ||
|
||
@Query(""" | ||
select m.sender as sender, m.receiver as receiver | ||
from Message m | ||
group by m.sender, m.receiver | ||
having m.sender = :member or m.receiver = :member | ||
""") | ||
List<MessageSenderReceiverDto> findBySenderOrReceiverOrderByIdDesc(Member member); | ||
|
||
@Query(""" | ||
select m.content as content, m.createdAt as createdAt, | ||
case when m.sender.id = :memberId then true else false end as isMine | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 별게 다있네요ㅋㅋㅋ 배워갑니다 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 우리가 예전에 우려했던 대로 Pageable을 사용하면 새로운 dm을 보낼 때 과거의 메시지가 중복돼서 보이는 문제가 있을 수 있겠네요.. |
||
from Message m | ||
where (m.sender.id = :memberId and m.receiver.id = :opponentId) | ||
or (m.sender.id = :opponentId and m.receiver.id = :memberId) | ||
order by m.id desc | ||
""") | ||
Slice<MessageListDto> findAllMessageByMemberAndPaging(Long memberId, Long opponentId, Pageable pageable); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.yigongil.backend.domain.message; | ||
|
||
import com.yigongil.backend.domain.member.Member; | ||
|
||
public interface MessageSenderReceiverDto { | ||
|
||
Member getSender(); | ||
|
||
Member getReceiver(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package com.yigongil.backend.domain.message; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertAll; | ||
|
||
import com.yigongil.backend.config.JpaConfig; | ||
import com.yigongil.backend.domain.member.Member; | ||
import com.yigongil.backend.domain.member.MemberRepository; | ||
import com.yigongil.backend.fixture.MemberFixture; | ||
import java.util.List; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; | ||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; | ||
import org.springframework.context.annotation.Import; | ||
import org.springframework.data.domain.PageRequest; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
|
||
@Import(JpaConfig.class) | ||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 안쓰면 어떻게 되나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 부분과 아래 Application.yml을 변경한 부분과 연관관계가 있는 것 같은데 저도 궁금하네요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
@DataJpaTest | ||
class MessageRepositoryTest { | ||
|
||
@Autowired | ||
private MessageRepository messageRepository; | ||
|
||
@Autowired | ||
private MemberRepository memberRepository; | ||
|
||
private Member 김진우; | ||
private Member 노이만; | ||
private Pageable pageable; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
김진우 = memberRepository.save(MemberFixture.김진우.toMember()); | ||
노이만 = memberRepository.save(MemberFixture.폰노이만.toMember()); | ||
pageable = PageRequest.of(0, 10); | ||
} | ||
|
||
@Test | ||
void 메시지를_주고받은_상대들을_조회하는_쿼리() { | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 노이만, 김진우, "Hello, world!")); | ||
|
||
List<MessageSenderReceiverDto> result = messageRepository.findBySenderOrReceiverOrderByIdDesc(김진우); | ||
|
||
assertThat(result).hasSize(2); | ||
} | ||
|
||
@Test | ||
void 메시지를_주고받은_상대들을_조회하는_쿼리2() { | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 노이만, 김진우, "Hello, world!")); | ||
|
||
List<MessageSenderReceiverDto> result = messageRepository.findBySenderOrReceiverOrderByIdDesc(김진우); | ||
|
||
assertThat(result).hasSize(2); | ||
} | ||
|
||
@Test | ||
void 메시지를_주고받은_상대들을_조회하는_쿼리3() { | ||
Member 파울러 = memberRepository.save(MemberFixture.마틴파울러.toMemberWithoutId()); | ||
|
||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 노이만, 김진우, "Hello, world!")); | ||
messageRepository.save(new Message(null, 파울러, 김진우, "Hello, world!")); | ||
|
||
List<MessageSenderReceiverDto> result = messageRepository.findBySenderOrReceiverOrderByIdDesc(김진우); | ||
|
||
assertThat(result).hasSize(3); | ||
} | ||
|
||
@Test | ||
void 메시지를_주고받은_상대들을_조회하는_쿼리4() { | ||
Member 파울러 = memberRepository.save(MemberFixture.마틴파울러.toMemberWithoutId()); | ||
|
||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 노이만, 김진우, "Hello, world!")); | ||
messageRepository.save(new Message(null, 파울러, 김진우, "Hello, world!")); | ||
messageRepository.save(new Message(null, 노이만, 파울러, "Hello, world!")); | ||
|
||
List<MessageSenderReceiverDto> result = messageRepository.findBySenderOrReceiverOrderByIdDesc(김진우); | ||
|
||
assertThat(result).hasSize(3); | ||
} | ||
|
||
@Test | ||
void 주고받은_쪽지를_정렬한다() { | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
messageRepository.save(new Message(null, 노이만, 김진우, "Hello, world!")); | ||
|
||
Slice<MessageListDto> result = messageRepository.findAllMessageByMemberAndPaging(김진우.getId(), 노이만.getId(), pageable); | ||
|
||
assertAll( | ||
() -> assertThat(result).hasSize(3), | ||
() -> assertThat(result.getContent().get(0).getIsMine()).isFalse(), | ||
() -> assertThat(result.getContent().get(1).getIsMine()).isTrue(), | ||
() -> assertThat(result.getContent().get(2).getIsMine()).isTrue() | ||
); | ||
} | ||
|
||
@Test | ||
void 주고받은_쪽지를_페이징한다() { | ||
for (int i = 0; i < 11; i++) { | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
} | ||
|
||
Slice<MessageListDto> result = messageRepository.findAllMessageByMemberAndPaging(김진우.getId(), 노이만.getId(), pageable); | ||
|
||
assertAll( | ||
() -> assertThat(result).hasSize(pageable.getPageSize()), | ||
() -> assertThat(result.hasNext()).isTrue() | ||
); | ||
} | ||
|
||
@Test | ||
void 주고받은_쪽지를_페이징한다2() { | ||
for (int i = 0; i < 9; i++) { | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
} | ||
|
||
Slice<MessageListDto> result = messageRepository.findAllMessageByMemberAndPaging(김진우.getId(), 노이만.getId(), pageable); | ||
|
||
assertAll( | ||
() -> assertThat(result).hasSize(9), | ||
() -> assertThat(result.hasNext()).isFalse(), | ||
() -> assertThat(result).map(MessageListDto::getIsMine).doesNotContain(false) | ||
); | ||
} | ||
|
||
@Test | ||
void 파라미터_순서를_바꿔서_주고받은_쪽지를_페이징한다() { | ||
for (int i = 0; i < 9; i++) { | ||
messageRepository.save(new Message(null, 김진우, 노이만, "Hello, world!")); | ||
} | ||
|
||
Slice<MessageListDto> result = messageRepository.findAllMessageByMemberAndPaging(노이만.getId(), 김진우.getId(), pageable); | ||
|
||
assertAll( | ||
() -> assertThat(result).hasSize(9), | ||
() -> assertThat(result.hasNext()).isFalse(), | ||
() -> assertThat(result).map(MessageListDto::getIsMine).doesNotContain(true) | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
지금 쿼리는 다음과 같이 message 테이블을 풀스캔하네요.(type = all)
where 절을 사용해 수정하면 type = index merge 가 되어 필요한 컬럼만 효율적으로 스캔할 수 있습니다! 👍
추가로 이 정보도 페이징 할 것일면 order 절을 넣어주면 좋겠네요! (메서드 이름에 있는데 쿼리엔 없어요)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은데요? 그리고 unique 값을 뽑아내기 위해 group by를 쓰셨다면 distinct를 쓰시는 걸 추천드립니다.
성능도 더 좋고 의미적으로도 더 잘 전달될 것 같아요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
추가로 인덱스 머지 최적화에 대해 가볍게 정리해봤는데 혹시 도움이 될까 싶어 링크 추가합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋습니다