Spring Instagram clone (Comment, AOP, Bug fix)

Hyeonseop-Noh·2022년 4월 14일
0

Model

userId, imageId, content(length limits)
Didn't use native query.

public class Comment {

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

    @Column(length = 100, nullable = false)
    private String content;

    @JsonIgnoreProperties({"images"})
    @JoinColumn(name = "userId")
    @ManyToOne(fetch = FetchType.EAGER)
    private User user;

    @JoinColumn(name = "imageId")
    @ManyToOne(fetch = FetchType.EAGER)
    private Image image;

    private LocalDateTime createDate;

    @PrePersist
    public void createDate() {
        this.createDate = LocalDateTime.now();
    }

}

Controller

Call addCmt and deleteCmt from CommentService. BindingResult -> AOP proccess

public class CommentApiController {

    private final CommentService commentService;

    @PostMapping("/api/comment")
    public ResponseEntity<?> commentSave(@Valid @RequestBody CommentDto commentDto, BindingResult bindingResult, @AuthenticationPrincipal PrincipalDetails principalDetails) {
        Comment comment = commentService.addCmt(commentDto.getContent(), commentDto.getImageId(), principalDetails.getUser().getId());
        return new ResponseEntity<>(new CMRespDto<>(1, "adding comment success", comment), HttpStatus.CREATED);
    }

    @DeleteMapping("/api/comment/{id}")
    public ResponseEntity<?> commentDelete(@PathVariable int id) {
        commentService.deleteCmt(id);
        return new ResponseEntity<>(new CMRespDto<>(1, "deleting comment success", null), HttpStatus.OK);
    }

}v

Service

Get the id from Image object and User object(for imageId and userId). When making object, insert id only. Therefore, it just returns image and user objects which have only id value. Set the comment by using setter(content, image, userEntity).

public class CommentService {

    private final CommentRepository commentRepository;
    private final UserRepository userRepository;

    @Transactional
    public Comment addCmt(String content, int imageId, int userId) {

        Image image = new Image();
        image.setId(imageId);

        User userEntity = userRepository.findById(userId).orElseThrow(() -> {
            throw new CustomApiException("Can't find user id");
        });

        Comment comment = new Comment();
        comment.setContent(content);
        comment.setImage(image);
        comment.setUser(userEntity);

        return commentRepository.save(comment);
    }

    @Transactional
    public void deleteCmt(int id) {
        try { // when it occurs error
            commentRepository.deleteById(id);
        } catch(Exception e) {
            throw new CustomApiException(e.getMessage());
        }
    }

}

AOP

proceedingJoinPoint: In apiController, can access to all target. apiAdvice pre execute than function in apiController.

public class ValidationAdvice {

    @Around("execution(* com.cos.photogram.web.api.*Controller.*(..))")
    public Object apiAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Object[] args = proceedingJoinPoint.getArgs();

        for (Object arg: args) {
            if(arg instanceof BindingResult) {
                BindingResult bindingResult = (BindingResult) arg;

                if (bindingResult.hasErrors()) {
                    Map<String, String> errorMap = new HashMap<>();
                    for (FieldError error : bindingResult.getFieldErrors()) {
                        errorMap.put(error.getField(), error.getDefaultMessage());
                    }
                    throw new CustomValidationApiException("Validation check failed", errorMap);
                }
            }
        }

        return proceedingJoinPoint.proceed(); // execute origin function
    }

    @Around("execution(* com.cos.photogram.web.*Controller.*(..))")
    public Object advice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Object[] args = proceedingJoinPoint.getArgs();

        for (Object arg: args) {
            if(arg instanceof BindingResult) {
                BindingResult bindingResult = (BindingResult) arg;

                if (bindingResult.hasErrors()) { // pre-processing
                    Map<String, String> errorMap = new HashMap<>();
                    for (FieldError error : bindingResult.getFieldErrors()) {
                        errorMap.put(error.getField(), error.getDefaultMessage());
                    }
                    throw new CustomValidationException("Validation check failed", errorMap);
                }
            }
        }

        return proceedingJoinPoint.proceed();
    }

}

Bug fix

Prob. Like button infinite recursion

When like button is on active, it can't load the image.

Sol. JsonIgnoreProperties

In Image domain, it imports likes and likes imports image again. Therefore, when imporing likes, add this code

@JsonIgnoreProperties({"image"})

Result:

profile
PlanBy Developer

2개의 댓글

comment-user-thumbnail
2022년 10월 21일

Did you follow any specific guides you could offer because slope game I've been meaning to create my own social media clone.

답글 달기
comment-user-thumbnail
2023년 4월 26일

Before your car is shipped, there are a few things you should do to prepare it. Remove any personal items from the car, and make sure the gas tank is less than a quarter full. Clean the car inside and out, and take photos of any existing damage. This will help ensure that any damage that occurs during shipping is documented.
https://suncruisermedia.com/suncruisermedia-blogs/diversions/how-much-does-it-cost-to-ship-a-car/

답글 달기