저번주에 공부한 데이터베이스의 구성을 잘해야 하는 이유 → 쿼리를 쉽게 쓰기 위해서
워크북에도 나와있듯, 테이블을 어떻게 구성하냐에 따라 쿼리의 길이와 복잡도가 달라집니다
두 테이블에서 조건에 맞는 레코드(튜플)를 결합합니다.
조건에 맞지 않는 레코드는 결과에서 제외됩니다.
❗두 테이블에 모두 지정한 열의 데이터가 있어야 합니다
보통 JOIN하면 INNER JOIN을 의미합니다
SELECT <열 목록>
FROM <첫 번째 테이블>
INNER JOIN <두 번째 테이블>
ON <조인 조건>
[WHERE 검색 조건]
#INNER JOIN을 JOIN이라고만 써도 INNER JOIN으로 인식합니다.
Member:
| member_id(PK) | name |
|---|---|
| 1 | a |
| 2 | b |
| 3 | c |
POST:
| post_id(PK) | title | author_id(FK) |
|---|---|---|
| 1 | hi | 1 |
| 2 | hello | 1 |
| 3 | bye | 4 |
author_id = member_id를 이용해 INNER JOIN 하면
| member_id | name | post_id | title | author_id |
|---|---|---|---|---|
| 1 | a | 1 | hi | 1 |
| 1 | a | 2 | hello | 1 |
SELECT <열 목록>
FROM <첫 번째 테이블(LEFT 테이블)>
<LEFT | RIGHT | FULL> OUTER JOIN <두 번째 테이블(RIGHT 테이블)>
ON <조인 조건>
[WHERE 검색 조건]
코드로 왼쪽,오른쪽 테이블의 구분 어려울 때
→ A <LEFT | RIGHT | FULL> OUTER JOIN B 라고 생각하면 됨
| member_id(PK) | name |
|---|---|
| 1 | a |
| 2 | b |
| 3 | c |
| post_id(PK) | title | author_id(FK) |
|---|---|---|
| 1 | hi | 1 |
| 2 | hello | 1 |
| 3 | bye | 4 |
author_id = member_id를 이용해 LEFT OUTER JOIN 하면
| member_id | name | post_id | title | author_id |
|---|---|---|---|---|
| 1 | a | 1 | hi | 1 |
| 1 | a | 2 | hello | 1 |
| 2 | b | null | null | null |
| 3 | c | null | null | null |
| member_id | name | post_id | title | author_id |
|---|---|---|---|---|
| 1 | a | 1 | hi | 1 |
| 1 | a | 2 | hello | 1 |
| null | null | 3 | bye | 4 |
| member_id | name | post_id | title | author_id |
|---|---|---|---|---|
| 1 | a | 1 | hi | 1 |
| 1 | a | 2 | hello | 1 |
| 2 | b | null | null | null |
| 3 | c | null | null | null |
| null | null | 3 | bye | 4 |
→ 행의 순서는 ORDER BY를 이용해 제어 가능(위의 경우는 member_id 순서, 그 이후 author_id 순서)
SELECT *
FROM <첫 번째 테이블>
CROSS JOIN <두 번째 테이블>
각 테이블의 모든 행을 이어붙인다 라고 이해하면됩니다(조건X) 위에서의 두 테이블을 이용하면
3 X 3이 되니 행이 9개짜리인 테이블이 만들어집니다
그냥 스스로를 INNER JOIN 하면 됩니다
서브쿼리를 활용하면 데이터를 다양한 방식으로 분석하고 필터링하는 등의 복잡한 연산을 수행할 수 있습니다
페이지 번호와 객체 개수를 이용
JPA의 Pageable 클래스(인터페이스)가 offset 페이징 방식
public PageDto getAllPosts(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
Page<Post> postPage = postRepository.findAll(pageable);
boolean hasNext = postPage.hasNext();
List<PostResponseDto> posts = postPage.getContent().stream()
.map(PostResponseDto::of)
.collect(Collectors.toList());
return PageDto.builder()
.posts(posts)
.hasNext(hasNext)
.build();
}
Page객체 전체를 출력해도 되지만 PageDto를 이용해 필요한 정보만 출력!
createdAt을 기준으로 DESC정렬(가장 최근에 작성한 글이 가장 위에 오도록)
모바일 환경에서의 무한 스크롤 방식(인스타그램)이 커서 페이징 방식
JPA에서 직접적으로 지원하진 않고 워크북처럼 쿼리 작성으로 구현
이름은 쿼리지만 엄밀히 말하면 살짝 다르다
JPA → JPQL(SQL을 추상화한 객체지향언어)
실제 SQL을 사용하려면 nativeQuery = true로 해주면 된다
@Query("SELECT p FROM Post p INNER JOIN p.author a WHERE a.member_id = :memberId")
List<Post> findAllPostsByAuthorMemberI(@Param("memberId") Long memberId);
위에서 써본 커서 페이징도 쿼리로 구현 가능하다
@Query(value = "SELECT * FROM posts WHERE post_id > :lastPostId ORDER BY createdAt DESC LIMIT 10", nativeQuery = true)
List<Post> findPostsForNextPage(@Param("lastPostId") Long lastPostId);
어땠나요..?

요런 느낌
@NoArgsConstructor JPA(Hibernate)는 엔터티 객체를 생성할 때 기본 생성자(파라미터가 없는 생성자)를 사용하여 인스턴스를 생성하게 됩니다. 따라서 엔터티 클래스는 항상 기본 생성자를 가지고 있어야 합니다. 만약 기본 생성자가 없다면, JPA는 엔터티 인스턴스를 생성하지 못하고, 동작하지 않게 됩니다. @AllArgsConstructor 코드 상에서 직접 엔터티 객체를 생성할 때 사용하는 것입니다. 이 생성자가 없다면, 개발자가 엔터티를 생성하여 사용할 때 모든 필드를 따로 setter 메서드로 설정해야 합니다. Member member = new Member();
member.setMemberId(id);
member.setName(name);
member.setEmail(email); 이렇게 생성하던 객체Member member = new Member(id, name, email); 만약 둘 다 없다면?@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;
private String name;
private String email;
// 기본 생성자
public Member() {
}
// 모든 필드를 파라미터로 가지는 생성자
public Member(Long memberId, String name, String email) {
this.memberId = memberId;
this.name = name;
this.email = email;
}of 메서드는 엔터티 인스턴스를 받아서, 해당 엔터티의 데이터를 사용해 ResponseDto의 인스턴스를 생성하고 반환하는 역할을 합니다. 즉, 엔터티 객체를 ResponseDto 객체로 변환하는 역할을 하는 메서드입니다. static 키워드를 사용한 이유는 of 메서드를 인스턴스 생성 없이 클래스 레벨에서 바로 호출할 수 있게 만들기 위함입니다@JsonIgnore를 사용하지 않으면 Member 엔터티와 PostLike 리스트가 JSON으로 직렬화 될 때 불필요한 데이터까지 API 응답에 포함될 수 있습니다. 또한, 양방향 관계에서 순환 참조 문제도 발생할 수 있습니다.