Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 참고X
- python자료형
- Git
- 도커
- 코테
- 스택
- 운체 1주차
- 파이썬 알고리즘 인터뷰
- 데베시 1주차
- 5장
- 최단거리
- 인스타
- 자료구조
- c언어 제어문
- 백준
- c언어 기본
- 코딩테스트
- git오류
- Workbench
- 인텔리제이
- 4장
- git 오류
- #코린이 #코딩 #할 수 있다
- DP
- 그리디
- c언어
- git기초
- python기초
- python기본
- 1주차(1)
Archives
- Today
- Total
하루살이 개발자
[Instagram 클론코딩] 4. 구독 본문
연관관계 개념
1. N : 1
- N인 테이블이 FK이다.(공식1)
- User기준 1 : N, 게시글 기준 1 : 1
2. N : N
- 중간 테이블을 만들어야 한다.(공식2)
- User와 영화의 관계에서 "예매"라는 중간 테이블을 만들어야 한다.
- User기준 1 : N , Movie기준 1 : N
1. 구독하기 모델 만들기
Subscribe
package com.cos.photogramstart.domain.subscribe;
import java.time.LocalDateTime;
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 javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import com.cos.photogramstart.domain.user.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table( // 유니크하게 만들기(1번이 2번 구독, 2번이 1번 구독 하는 중복상황을 막기 위해)
uniqueConstraints = {
@UniqueConstraint(
name="subscribe_uk",
columnNames = {"fromUserId", "toUserId"} // 2개를 유니크하게 만들기
)
}
)
public class Subscribe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@JoinColumn(name = "fromUserId") // 스키마 변경: DB에서 언더바 방식(fromUser_id) 맘에 안들어서 바꿈 (이렇게 컬럼명 만들어! 니 맘대로 만들지 말고!!)
@ManyToOne // 자동으로 테이블 생성 N : 1 (ORM 방식: entity를 받아 바로 테이블로 만들어줌)
private User fromUser; // 구독 하는 user
@JoinColumn(name = "toUserId")
@ManyToOne
private User toUser; // 구독 받는 user
private LocalDateTime createDate;
@PrePersist
public void createDate() {
this.createDate = LocalDateTime.now();
}
}
SubscribeRepository
package com.cos.photogramstart.domain.subscribe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
public interface SubscribeRepository extends JpaRepository<Subscribe, Integer>{
}
DB 확인
SELECT * FROM subscribe;
INSERT INTO subscribe(fromUserId, toUserId) VALUES(2, 1);
DROP TABLE subscribe;
2. 구독, 구독취소 API 만들기
SubscribeService
package com.cos.photogramstart.service;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.qlrm.mapper.JpaResultMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.handler.ex.CustomApiException;
import com.cos.photogramstart.web.dto.subscribe.SubscribeDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class SubscribeService {
private final SubscribeRepository subscribeRepository;
private final EntityManager em; // Repository는 EntityManager를 구현해서 만들어져 있는 구현체
@Transactional // DB에 영향을 주기 때문에
public void 구독하기(int fromUserId, int toUserId) {
try {
subscribeRepository.mSubscribe(fromUserId, toUserId);
} catch (Exception e) {
throw new CustomApiException("이미 구독을 하였습니다."); // 핸들러에서 처리
}
// save를 이용하려면 객체가 int로 정의되어있어야 하는데,
// subscribe는 User로(오브젝트로) 정의 되어있으므로 이렇게 하지 말고 직접 네이티브 커리를 짜자(SubscribeRepository에 짜기!)
//subscribeRepository.save(null);
}
@Transactional
public void 구독취소하기(int fromUserId, int toUserId) {
subscribeRepository.mUnSubscribe(fromUserId, toUserId);
}
}
SubscribeRepository
package com.cos.photogramstart.domain.subscribe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
public interface SubscribeRepository extends JpaRepository<Subscribe, Integer>{
// 구독 쿼리 직접 만들기!
// Scribe에서 생성했던 date는 작접 작성하는 쿼리에서는 작동 안 함 -> createDate 필요
@Modifying // INSERT, DELETE, UPDATE 를 네이티브 쿼리로 작성하려면 해당 어노테이션 필요!!
@Query(value = "INSERT INTO subscribe(fromUserId, toUserId, createDate) VALUES(:fromUserId, :toUserId, now())", nativeQuery = true)
void mSubscribe(int fromUserId, int toUserId); // 성공하면 1리턴(변경된 행의 개수가 리턴됨, 0이면? 변경된 행이 없다는 것), 실패하면 -1 리턴
// 내가 만들었다는 의미에서 m붙임(mSubscribe)
// 구독 취소 쿼리 직접 만들기!
@Modifying
@Query(value = "DELETE FROM subscribe WHERE fromUserId = :fromUserId AND toUserId = :toUserId", nativeQuery = true)
void mUnSubscribe(int fromUserId, int toUserId);
}
SubscribeApiController
package com.cos.photogramstart.web.api;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.service.SubscribeService;
import com.cos.photogramstart.web.dto.CMRespDto;
import lombok.RequiredArgsConstructor;
// 파일 등이 아닌 데이터만 리턴하는 컨트롤러를 apiController라고 함
@RequiredArgsConstructor
@RestController
public class SubscribeApiController {
private final SubscribeService subscribeService;
// 현재 로그인 한 사람이 구독함 함
@PostMapping("/api/subscribe/{toUserId}")
public ResponseEntity<?> subscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int toUserId){
subscribeService.구독하기(principalDetails.getUser().getId(), toUserId);
return new ResponseEntity<>(new CMRespDto<>(1, "구독하기 성공", null), HttpStatus.OK);
}
// 현재 로그인 한 사람이 구독취소 함
@DeleteMapping("/api/subscribe/{toUserId}")
public ResponseEntity<?> unSubscribe(@AuthenticationPrincipal PrincipalDetails principalDetails, @PathVariable int toUserId){
subscribeService.구독취소하기(principalDetails.getUser().getId(), toUserId);
return new ResponseEntity<>(new CMRespDto<>(1, "구독취소하기 성공", null), HttpStatus.OK);
}
}
'Project > Instagram 클론코딩' 카테고리의 다른 글
[Instagram 클론코딩] 4. 회원정보 수정 (0) | 2022.02.16 |
---|---|
[Instagram 클론코딩] 3. 로그인 (0) | 2022.02.15 |
[Instagram 클론코딩] 2. 회원가입 (0) | 2022.02.14 |
[Instagram 클론코딩] 0. 설계 (0) | 2022.02.14 |